2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7312 * @class Roo.grid.AbstractSelectionModel
7313 * @extends Roo.util.Observable
7314 * Abstract base class for grid SelectionModels. It provides the interface that should be
7315 * implemented by descendant classes. This class should not be directly instantiated.
7318 Roo.grid.AbstractSelectionModel = function(){
7319 this.locked = false;
7320 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7324 /** @ignore Called by the grid automatically. Do not call directly. */
7325 init : function(grid){
7331 * Locks the selections.
7338 * Unlocks the selections.
7340 unlock : function(){
7341 this.locked = false;
7345 * Returns true if the selections are locked.
7348 isLocked : function(){
7353 * Ext JS Library 1.1.1
7354 * Copyright(c) 2006-2007, Ext JS, LLC.
7356 * Originally Released Under LGPL - original licence link has changed is not relivant.
7359 * <script type="text/javascript">
7362 * @extends Roo.grid.AbstractSelectionModel
7363 * @class Roo.grid.RowSelectionModel
7364 * The default SelectionModel used by {@link Roo.grid.Grid}.
7365 * It supports multiple selections and keyboard selection/navigation.
7367 * @param {Object} config
7369 Roo.grid.RowSelectionModel = function(config){
7370 Roo.apply(this, config);
7371 this.selections = new Roo.util.MixedCollection(false, function(o){
7376 this.lastActive = false;
7380 * @event selectionchange
7381 * Fires when the selection changes
7382 * @param {SelectionModel} this
7384 "selectionchange" : true,
7386 * @event afterselectionchange
7387 * Fires after the selection changes (eg. by key press or clicking)
7388 * @param {SelectionModel} this
7390 "afterselectionchange" : true,
7392 * @event beforerowselect
7393 * Fires when a row is selected being selected, return false to cancel.
7394 * @param {SelectionModel} this
7395 * @param {Number} rowIndex The selected index
7396 * @param {Boolean} keepExisting False if other selections will be cleared
7398 "beforerowselect" : true,
7401 * Fires when a row is selected.
7402 * @param {SelectionModel} this
7403 * @param {Number} rowIndex The selected index
7404 * @param {Roo.data.Record} r The record
7408 * @event rowdeselect
7409 * Fires when a row is deselected.
7410 * @param {SelectionModel} this
7411 * @param {Number} rowIndex The selected index
7413 "rowdeselect" : true
7415 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416 this.locked = false;
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7421 * @cfg {Boolean} singleSelect
7422 * True to allow selection of only one row at a time (defaults to false)
7424 singleSelect : false,
7427 initEvents : function(){
7429 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430 this.grid.on("mousedown", this.handleMouseDown, this);
7431 }else{ // allow click to work like normal
7432 this.grid.on("rowclick", this.handleDragableRowClick, this);
7434 // bootstrap does not have a view..
7435 var view = this.grid.view ? this.grid.view : this.grid;
7436 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7439 this.selectPrevious(e.shiftKey);
7440 }else if(this.last !== false && this.lastActive !== false){
7441 var last = this.last;
7442 this.selectRange(this.last, this.lastActive-1);
7443 view.focusRow(this.lastActive);
7448 this.selectFirstRow();
7450 this.fireEvent("afterselectionchange", this);
7452 "down" : function(e){
7454 this.selectNext(e.shiftKey);
7455 }else if(this.last !== false && this.lastActive !== false){
7456 var last = this.last;
7457 this.selectRange(this.last, this.lastActive+1);
7458 view.focusRow(this.lastActive);
7463 this.selectFirstRow();
7465 this.fireEvent("afterselectionchange", this);
7471 view.on("refresh", this.onRefresh, this);
7472 view.on("rowupdated", this.onRowUpdated, this);
7473 view.on("rowremoved", this.onRemove, this);
7477 onRefresh : function(){
7478 var ds = this.grid.ds, i, v = this.grid.view;
7479 var s = this.selections;
7481 if((i = ds.indexOfId(r.id)) != -1){
7483 s.add(ds.getAt(i)); // updating the selection relate data
7491 onRemove : function(v, index, r){
7492 this.selections.remove(r);
7496 onRowUpdated : function(v, index, r){
7497 if(this.isSelected(r)){
7498 v.onRowSelect(index);
7504 * @param {Array} records The records to select
7505 * @param {Boolean} keepExisting (optional) True to keep existing selections
7507 selectRecords : function(records, keepExisting){
7509 this.clearSelections();
7511 var ds = this.grid.ds;
7512 for(var i = 0, len = records.length; i < len; i++){
7513 this.selectRow(ds.indexOf(records[i]), true);
7518 * Gets the number of selected rows.
7521 getCount : function(){
7522 return this.selections.length;
7526 * Selects the first row in the grid.
7528 selectFirstRow : function(){
7533 * Select the last row.
7534 * @param {Boolean} keepExisting (optional) True to keep existing selections
7536 selectLastRow : function(keepExisting){
7537 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7541 * Selects the row immediately following the last selected row.
7542 * @param {Boolean} keepExisting (optional) True to keep existing selections
7544 selectNext : function(keepExisting){
7545 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7546 this.selectRow(this.last+1, keepExisting);
7547 var view = this.grid.view ? this.grid.view : this.grid;
7548 view.focusRow(this.last);
7553 * Selects the row that precedes the last selected row.
7554 * @param {Boolean} keepExisting (optional) True to keep existing selections
7556 selectPrevious : function(keepExisting){
7558 this.selectRow(this.last-1, keepExisting);
7559 var view = this.grid.view ? this.grid.view : this.grid;
7560 view.focusRow(this.last);
7565 * Returns the selected records
7566 * @return {Array} Array of selected records
7568 getSelections : function(){
7569 return [].concat(this.selections.items);
7573 * Returns the first selected record.
7576 getSelected : function(){
7577 return this.selections.itemAt(0);
7582 * Clears all selections.
7584 clearSelections : function(fast){
7589 var ds = this.grid.ds;
7590 var s = this.selections;
7592 this.deselectRow(ds.indexOfId(r.id));
7596 this.selections.clear();
7605 selectAll : function(){
7609 this.selections.clear();
7610 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7611 this.selectRow(i, true);
7616 * Returns True if there is a selection.
7619 hasSelection : function(){
7620 return this.selections.length > 0;
7624 * Returns True if the specified row is selected.
7625 * @param {Number/Record} record The record or index of the record to check
7628 isSelected : function(index){
7629 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7630 return (r && this.selections.key(r.id) ? true : false);
7634 * Returns True if the specified record id is selected.
7635 * @param {String} id The id of record to check
7638 isIdSelected : function(id){
7639 return (this.selections.key(id) ? true : false);
7643 handleMouseDown : function(e, t)
7645 var view = this.grid.view ? this.grid.view : this.grid;
7647 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7650 if(e.shiftKey && this.last !== false){
7651 var last = this.last;
7652 this.selectRange(last, rowIndex, e.ctrlKey);
7653 this.last = last; // reset the last
7654 view.focusRow(rowIndex);
7656 var isSelected = this.isSelected(rowIndex);
7657 if(e.button !== 0 && isSelected){
7658 view.focusRow(rowIndex);
7659 }else if(e.ctrlKey && isSelected){
7660 this.deselectRow(rowIndex);
7661 }else if(!isSelected){
7662 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7663 view.focusRow(rowIndex);
7666 this.fireEvent("afterselectionchange", this);
7669 handleDragableRowClick : function(grid, rowIndex, e)
7671 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7672 this.selectRow(rowIndex, false);
7673 var view = this.grid.view ? this.grid.view : this.grid;
7674 view.focusRow(rowIndex);
7675 this.fireEvent("afterselectionchange", this);
7680 * Selects multiple rows.
7681 * @param {Array} rows Array of the indexes of the row to select
7682 * @param {Boolean} keepExisting (optional) True to keep existing selections
7684 selectRows : function(rows, keepExisting){
7686 this.clearSelections();
7688 for(var i = 0, len = rows.length; i < len; i++){
7689 this.selectRow(rows[i], true);
7694 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7695 * @param {Number} startRow The index of the first row in the range
7696 * @param {Number} endRow The index of the last row in the range
7697 * @param {Boolean} keepExisting (optional) True to retain existing selections
7699 selectRange : function(startRow, endRow, keepExisting){
7704 this.clearSelections();
7706 if(startRow <= endRow){
7707 for(var i = startRow; i <= endRow; i++){
7708 this.selectRow(i, true);
7711 for(var i = startRow; i >= endRow; i--){
7712 this.selectRow(i, true);
7718 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7719 * @param {Number} startRow The index of the first row in the range
7720 * @param {Number} endRow The index of the last row in the range
7722 deselectRange : function(startRow, endRow, preventViewNotify){
7726 for(var i = startRow; i <= endRow; i++){
7727 this.deselectRow(i, preventViewNotify);
7733 * @param {Number} row The index of the row to select
7734 * @param {Boolean} keepExisting (optional) True to keep existing selections
7736 selectRow : function(index, keepExisting, preventViewNotify){
7737 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7740 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7741 if(!keepExisting || this.singleSelect){
7742 this.clearSelections();
7744 var r = this.grid.ds.getAt(index);
7745 this.selections.add(r);
7746 this.last = this.lastActive = index;
7747 if(!preventViewNotify){
7748 var view = this.grid.view ? this.grid.view : this.grid;
7749 view.onRowSelect(index);
7751 this.fireEvent("rowselect", this, index, r);
7752 this.fireEvent("selectionchange", this);
7758 * @param {Number} row The index of the row to deselect
7760 deselectRow : function(index, preventViewNotify){
7764 if(this.last == index){
7767 if(this.lastActive == index){
7768 this.lastActive = false;
7770 var r = this.grid.ds.getAt(index);
7771 this.selections.remove(r);
7772 if(!preventViewNotify){
7773 var view = this.grid.view ? this.grid.view : this.grid;
7774 view.onRowDeselect(index);
7776 this.fireEvent("rowdeselect", this, index);
7777 this.fireEvent("selectionchange", this);
7781 restoreLast : function(){
7783 this.last = this._last;
7788 acceptsNav : function(row, col, cm){
7789 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7793 onEditorKey : function(field, e){
7794 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7799 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7801 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7803 }else if(k == e.ENTER && !e.ctrlKey){
7807 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7809 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7811 }else if(k == e.ESC){
7815 g.startEditing(newCell[0], newCell[1]);
7820 * Ext JS Library 1.1.1
7821 * Copyright(c) 2006-2007, Ext JS, LLC.
7823 * Originally Released Under LGPL - original licence link has changed is not relivant.
7826 * <script type="text/javascript">
7831 * @class Roo.grid.ColumnModel
7832 * @extends Roo.util.Observable
7833 * This is the default implementation of a ColumnModel used by the Grid. It defines
7834 * the columns in the grid.
7837 var colModel = new Roo.grid.ColumnModel([
7838 {header: "Ticker", width: 60, sortable: true, locked: true},
7839 {header: "Company Name", width: 150, sortable: true},
7840 {header: "Market Cap.", width: 100, sortable: true},
7841 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7842 {header: "Employees", width: 100, sortable: true, resizable: false}
7847 * The config options listed for this class are options which may appear in each
7848 * individual column definition.
7849 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7851 * @param {Object} config An Array of column config objects. See this class's
7852 * config objects for details.
7854 Roo.grid.ColumnModel = function(config){
7856 * The config passed into the constructor
7858 this.config = []; //config;
7861 // if no id, create one
7862 // if the column does not have a dataIndex mapping,
7863 // map it to the order it is in the config
7864 for(var i = 0, len = config.length; i < len; i++){
7865 this.addColumn(config[i]);
7870 * The width of columns which have no width specified (defaults to 100)
7873 this.defaultWidth = 100;
7876 * Default sortable of columns which have no sortable specified (defaults to false)
7879 this.defaultSortable = false;
7883 * @event widthchange
7884 * Fires when the width of a column changes.
7885 * @param {ColumnModel} this
7886 * @param {Number} columnIndex The column index
7887 * @param {Number} newWidth The new width
7889 "widthchange": true,
7891 * @event headerchange
7892 * Fires when the text of a header changes.
7893 * @param {ColumnModel} this
7894 * @param {Number} columnIndex The column index
7895 * @param {Number} newText The new header text
7897 "headerchange": true,
7899 * @event hiddenchange
7900 * Fires when a column is hidden or "unhidden".
7901 * @param {ColumnModel} this
7902 * @param {Number} columnIndex The column index
7903 * @param {Boolean} hidden true if hidden, false otherwise
7905 "hiddenchange": true,
7907 * @event columnmoved
7908 * Fires when a column is moved.
7909 * @param {ColumnModel} this
7910 * @param {Number} oldIndex
7911 * @param {Number} newIndex
7913 "columnmoved" : true,
7915 * @event columlockchange
7916 * Fires when a column's locked state is changed
7917 * @param {ColumnModel} this
7918 * @param {Number} colIndex
7919 * @param {Boolean} locked true if locked
7921 "columnlockchange" : true
7923 Roo.grid.ColumnModel.superclass.constructor.call(this);
7925 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7927 * @cfg {String} header The header text to display in the Grid view.
7930 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7931 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7932 * specified, the column's index is used as an index into the Record's data Array.
7935 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7936 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7939 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7940 * Defaults to the value of the {@link #defaultSortable} property.
7941 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7944 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7947 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7950 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7953 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7956 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7957 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7958 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7959 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7962 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7965 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7968 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7971 * @cfg {String} cursor (Optional)
7974 * @cfg {String} tooltip (Optional)
7977 * @cfg {Number} xs (Optional)
7980 * @cfg {Number} sm (Optional)
7983 * @cfg {Number} md (Optional)
7986 * @cfg {Number} lg (Optional)
7989 * Returns the id of the column at the specified index.
7990 * @param {Number} index The column index
7991 * @return {String} the id
7993 getColumnId : function(index){
7994 return this.config[index].id;
7998 * Returns the column for a specified id.
7999 * @param {String} id The column id
8000 * @return {Object} the column
8002 getColumnById : function(id){
8003 return this.lookup[id];
8008 * Returns the column Object for a specified dataIndex.
8009 * @param {String} dataIndex The column dataIndex
8010 * @return {Object|Boolean} the column or false if not found
8012 getColumnByDataIndex: function(dataIndex){
8013 var index = this.findColumnIndex(dataIndex);
8014 return index > -1 ? this.config[index] : false;
8018 * Returns the index for a specified column id.
8019 * @param {String} id The column id
8020 * @return {Number} the index, or -1 if not found
8022 getIndexById : function(id){
8023 for(var i = 0, len = this.config.length; i < len; i++){
8024 if(this.config[i].id == id){
8032 * Returns the index for a specified column dataIndex.
8033 * @param {String} dataIndex The column dataIndex
8034 * @return {Number} the index, or -1 if not found
8037 findColumnIndex : function(dataIndex){
8038 for(var i = 0, len = this.config.length; i < len; i++){
8039 if(this.config[i].dataIndex == dataIndex){
8047 moveColumn : function(oldIndex, newIndex){
8048 var c = this.config[oldIndex];
8049 this.config.splice(oldIndex, 1);
8050 this.config.splice(newIndex, 0, c);
8051 this.dataMap = null;
8052 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8055 isLocked : function(colIndex){
8056 return this.config[colIndex].locked === true;
8059 setLocked : function(colIndex, value, suppressEvent){
8060 if(this.isLocked(colIndex) == value){
8063 this.config[colIndex].locked = value;
8065 this.fireEvent("columnlockchange", this, colIndex, value);
8069 getTotalLockedWidth : function(){
8071 for(var i = 0; i < this.config.length; i++){
8072 if(this.isLocked(i) && !this.isHidden(i)){
8073 this.totalWidth += this.getColumnWidth(i);
8079 getLockedCount : function(){
8080 for(var i = 0, len = this.config.length; i < len; i++){
8081 if(!this.isLocked(i)){
8086 return this.config.length;
8090 * Returns the number of columns.
8093 getColumnCount : function(visibleOnly){
8094 if(visibleOnly === true){
8096 for(var i = 0, len = this.config.length; i < len; i++){
8097 if(!this.isHidden(i)){
8103 return this.config.length;
8107 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8108 * @param {Function} fn
8109 * @param {Object} scope (optional)
8110 * @return {Array} result
8112 getColumnsBy : function(fn, scope){
8114 for(var i = 0, len = this.config.length; i < len; i++){
8115 var c = this.config[i];
8116 if(fn.call(scope||this, c, i) === true){
8124 * Returns true if the specified column is sortable.
8125 * @param {Number} col The column index
8128 isSortable : function(col){
8129 if(typeof this.config[col].sortable == "undefined"){
8130 return this.defaultSortable;
8132 return this.config[col].sortable;
8136 * Returns the rendering (formatting) function defined for the column.
8137 * @param {Number} col The column index.
8138 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8140 getRenderer : function(col){
8141 if(!this.config[col].renderer){
8142 return Roo.grid.ColumnModel.defaultRenderer;
8144 return this.config[col].renderer;
8148 * Sets the rendering (formatting) function for a column.
8149 * @param {Number} col The column index
8150 * @param {Function} fn The function to use to process the cell's raw data
8151 * to return HTML markup for the grid view. The render function is called with
8152 * the following parameters:<ul>
8153 * <li>Data value.</li>
8154 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8155 * <li>css A CSS style string to apply to the table cell.</li>
8156 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8157 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8158 * <li>Row index</li>
8159 * <li>Column index</li>
8160 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8162 setRenderer : function(col, fn){
8163 this.config[col].renderer = fn;
8167 * Returns the width for the specified column.
8168 * @param {Number} col The column index
8171 getColumnWidth : function(col){
8172 return this.config[col].width * 1 || this.defaultWidth;
8176 * Sets the width for a column.
8177 * @param {Number} col The column index
8178 * @param {Number} width The new width
8180 setColumnWidth : function(col, width, suppressEvent){
8181 this.config[col].width = width;
8182 this.totalWidth = null;
8184 this.fireEvent("widthchange", this, col, width);
8189 * Returns the total width of all columns.
8190 * @param {Boolean} includeHidden True to include hidden column widths
8193 getTotalWidth : function(includeHidden){
8194 if(!this.totalWidth){
8195 this.totalWidth = 0;
8196 for(var i = 0, len = this.config.length; i < len; i++){
8197 if(includeHidden || !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8202 return this.totalWidth;
8206 * Returns the header for the specified column.
8207 * @param {Number} col The column index
8210 getColumnHeader : function(col){
8211 return this.config[col].header;
8215 * Sets the header for a column.
8216 * @param {Number} col The column index
8217 * @param {String} header The new header
8219 setColumnHeader : function(col, header){
8220 this.config[col].header = header;
8221 this.fireEvent("headerchange", this, col, header);
8225 * Returns the tooltip for the specified column.
8226 * @param {Number} col The column index
8229 getColumnTooltip : function(col){
8230 return this.config[col].tooltip;
8233 * Sets the tooltip for a column.
8234 * @param {Number} col The column index
8235 * @param {String} tooltip The new tooltip
8237 setColumnTooltip : function(col, tooltip){
8238 this.config[col].tooltip = tooltip;
8242 * Returns the dataIndex for the specified column.
8243 * @param {Number} col The column index
8246 getDataIndex : function(col){
8247 return this.config[col].dataIndex;
8251 * Sets the dataIndex for a column.
8252 * @param {Number} col The column index
8253 * @param {Number} dataIndex The new dataIndex
8255 setDataIndex : function(col, dataIndex){
8256 this.config[col].dataIndex = dataIndex;
8262 * Returns true if the cell is editable.
8263 * @param {Number} colIndex The column index
8264 * @param {Number} rowIndex The row index - this is nto actually used..?
8267 isCellEditable : function(colIndex, rowIndex){
8268 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8272 * Returns the editor defined for the cell/column.
8273 * return false or null to disable editing.
8274 * @param {Number} colIndex The column index
8275 * @param {Number} rowIndex The row index
8278 getCellEditor : function(colIndex, rowIndex){
8279 return this.config[colIndex].editor;
8283 * Sets if a column is editable.
8284 * @param {Number} col The column index
8285 * @param {Boolean} editable True if the column is editable
8287 setEditable : function(col, editable){
8288 this.config[col].editable = editable;
8293 * Returns true if the column is hidden.
8294 * @param {Number} colIndex The column index
8297 isHidden : function(colIndex){
8298 return this.config[colIndex].hidden;
8303 * Returns true if the column width cannot be changed
8305 isFixed : function(colIndex){
8306 return this.config[colIndex].fixed;
8310 * Returns true if the column can be resized
8313 isResizable : function(colIndex){
8314 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8317 * Sets if a column is hidden.
8318 * @param {Number} colIndex The column index
8319 * @param {Boolean} hidden True if the column is hidden
8321 setHidden : function(colIndex, hidden){
8322 this.config[colIndex].hidden = hidden;
8323 this.totalWidth = null;
8324 this.fireEvent("hiddenchange", this, colIndex, hidden);
8328 * Sets the editor for a column.
8329 * @param {Number} col The column index
8330 * @param {Object} editor The editor object
8332 setEditor : function(col, editor){
8333 this.config[col].editor = editor;
8336 * Add a column (experimental...) - defaults to adding to the end..
8337 * @param {Object} config
8339 addColumn : function(c)
8342 var i = this.config.length;
8345 if(typeof c.dataIndex == "undefined"){
8348 if(typeof c.renderer == "string"){
8349 c.renderer = Roo.util.Format[c.renderer];
8351 if(typeof c.id == "undefined"){
8354 if(c.editor && c.editor.xtype){
8355 c.editor = Roo.factory(c.editor, Roo.grid);
8357 if(c.editor && c.editor.isFormField){
8358 c.editor = new Roo.grid.GridEditor(c.editor);
8360 this.lookup[c.id] = c;
8365 Roo.grid.ColumnModel.defaultRenderer = function(value)
8367 if(typeof value == "object") {
8370 if(typeof value == "string" && value.length < 1){
8374 return String.format("{0}", value);
8377 // Alias for backwards compatibility
8378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8381 * Ext JS Library 1.1.1
8382 * Copyright(c) 2006-2007, Ext JS, LLC.
8384 * Originally Released Under LGPL - original licence link has changed is not relivant.
8387 * <script type="text/javascript">
8391 * @class Roo.LoadMask
8392 * A simple utility class for generically masking elements while loading data. If the element being masked has
8393 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8394 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8395 * element's UpdateManager load indicator and will be destroyed after the initial load.
8397 * Create a new LoadMask
8398 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8399 * @param {Object} config The config object
8401 Roo.LoadMask = function(el, config){
8402 this.el = Roo.get(el);
8403 Roo.apply(this, config);
8405 this.store.on('beforeload', this.onBeforeLoad, this);
8406 this.store.on('load', this.onLoad, this);
8407 this.store.on('loadexception', this.onLoadException, this);
8408 this.removeMask = false;
8410 var um = this.el.getUpdateManager();
8411 um.showLoadIndicator = false; // disable the default indicator
8412 um.on('beforeupdate', this.onBeforeLoad, this);
8413 um.on('update', this.onLoad, this);
8414 um.on('failure', this.onLoad, this);
8415 this.removeMask = true;
8419 Roo.LoadMask.prototype = {
8421 * @cfg {Boolean} removeMask
8422 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8423 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8427 * The text to display in a centered loading message box (defaults to 'Loading...')
8431 * @cfg {String} msgCls
8432 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8434 msgCls : 'x-mask-loading',
8437 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8443 * Disables the mask to prevent it from being displayed
8445 disable : function(){
8446 this.disabled = true;
8450 * Enables the mask so that it can be displayed
8452 enable : function(){
8453 this.disabled = false;
8456 onLoadException : function()
8460 if (typeof(arguments[3]) != 'undefined') {
8461 Roo.MessageBox.alert("Error loading",arguments[3]);
8465 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8466 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8473 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8478 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8482 onBeforeLoad : function(){
8484 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8489 destroy : function(){
8491 this.store.un('beforeload', this.onBeforeLoad, this);
8492 this.store.un('load', this.onLoad, this);
8493 this.store.un('loadexception', this.onLoadException, this);
8495 var um = this.el.getUpdateManager();
8496 um.un('beforeupdate', this.onBeforeLoad, this);
8497 um.un('update', this.onLoad, this);
8498 um.un('failure', this.onLoad, this);
8502 * @class Roo.bootstrap.Table
8504 * @extends Roo.bootstrap.Component
8505 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8506 * Similar to Roo.grid.Grid
8508 var table = Roo.factory({
8510 xns : Roo.bootstrap,
8511 autoSizeColumns: true,
8518 sortInfo : { direction : 'ASC', field: 'name' },
8520 xtype : 'HttpProxy',
8523 url : 'https://example.com/some.data.url.json'
8526 xtype : 'JsonReader',
8528 fields : [ 'id', 'name', whatever' ],
8535 xtype : 'ColumnModel',
8539 dataIndex : 'is_in_group',
8542 renderer : function(v, x , r) {
8544 return String.format("{0}", v)
8550 xtype : 'RowSelectionModel',
8551 xns : Roo.bootstrap.Table
8552 // you can add listeners to catch selection change here....
8558 grid.render(Roo.get("some-div"));
8561 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8566 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8567 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8568 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8570 * @cfg {String} cls table class
8573 * @cfg {boolean} striped Should the rows be alternative striped
8574 * @cfg {boolean} bordered Add borders to the table
8575 * @cfg {boolean} hover Add hover highlighting
8576 * @cfg {boolean} condensed Format condensed
8577 * @cfg {boolean} responsive Format condensed
8578 * @cfg {Boolean} loadMask (true|false) default false
8579 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8580 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8581 * @cfg {Boolean} rowSelection (true|false) default false
8582 * @cfg {Boolean} cellSelection (true|false) default false
8583 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8584 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8585 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8586 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8590 * Create a new Table
8591 * @param {Object} config The config object
8594 Roo.bootstrap.Table = function(config)
8596 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8599 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8600 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8601 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8602 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8604 this.view = this; // compat with grid.
8606 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8608 this.sm.grid = this;
8609 this.selModel = Roo.factory(this.sm, Roo.grid);
8610 this.sm = this.selModel;
8611 this.sm.xmodule = this.xmodule || false;
8614 if (this.cm && typeof(this.cm.config) == 'undefined') {
8615 this.colModel = new Roo.grid.ColumnModel(this.cm);
8616 this.cm = this.colModel;
8617 this.cm.xmodule = this.xmodule || false;
8620 this.store= Roo.factory(this.store, Roo.data);
8621 this.ds = this.store;
8622 this.ds.xmodule = this.xmodule || false;
8625 if (this.footer && this.store) {
8626 this.footer.dataSource = this.ds;
8627 this.footer = Roo.factory(this.footer);
8634 * Fires when a cell is clicked
8635 * @param {Roo.bootstrap.Table} this
8636 * @param {Roo.Element} el
8637 * @param {Number} rowIndex
8638 * @param {Number} columnIndex
8639 * @param {Roo.EventObject} e
8643 * @event celldblclick
8644 * Fires when a cell is double clicked
8645 * @param {Roo.bootstrap.Table} this
8646 * @param {Roo.Element} el
8647 * @param {Number} rowIndex
8648 * @param {Number} columnIndex
8649 * @param {Roo.EventObject} e
8651 "celldblclick" : true,
8654 * Fires when a row is clicked
8655 * @param {Roo.bootstrap.Table} this
8656 * @param {Roo.Element} el
8657 * @param {Number} rowIndex
8658 * @param {Roo.EventObject} e
8662 * @event rowdblclick
8663 * Fires when a row is double clicked
8664 * @param {Roo.bootstrap.Table} this
8665 * @param {Roo.Element} el
8666 * @param {Number} rowIndex
8667 * @param {Roo.EventObject} e
8669 "rowdblclick" : true,
8672 * Fires when a mouseover occur
8673 * @param {Roo.bootstrap.Table} this
8674 * @param {Roo.Element} el
8675 * @param {Number} rowIndex
8676 * @param {Number} columnIndex
8677 * @param {Roo.EventObject} e
8682 * Fires when a mouseout occur
8683 * @param {Roo.bootstrap.Table} this
8684 * @param {Roo.Element} el
8685 * @param {Number} rowIndex
8686 * @param {Number} columnIndex
8687 * @param {Roo.EventObject} e
8692 * Fires when a row is rendered, so you can change add a style to it.
8693 * @param {Roo.bootstrap.Table} this
8694 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8698 * @event rowsrendered
8699 * Fires when all the rows have been rendered
8700 * @param {Roo.bootstrap.Table} this
8702 'rowsrendered' : true,
8704 * @event contextmenu
8705 * The raw contextmenu event for the entire grid.
8706 * @param {Roo.EventObject} e
8708 "contextmenu" : true,
8710 * @event rowcontextmenu
8711 * Fires when a row is right clicked
8712 * @param {Roo.bootstrap.Table} this
8713 * @param {Number} rowIndex
8714 * @param {Roo.EventObject} e
8716 "rowcontextmenu" : true,
8718 * @event cellcontextmenu
8719 * Fires when a cell is right clicked
8720 * @param {Roo.bootstrap.Table} this
8721 * @param {Number} rowIndex
8722 * @param {Number} cellIndex
8723 * @param {Roo.EventObject} e
8725 "cellcontextmenu" : true,
8727 * @event headercontextmenu
8728 * Fires when a header is right clicked
8729 * @param {Roo.bootstrap.Table} this
8730 * @param {Number} columnIndex
8731 * @param {Roo.EventObject} e
8733 "headercontextmenu" : true,
8736 * The raw mousedown event for the entire grid.
8737 * @param {Roo.EventObject} e
8744 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8761 rowSelection : false,
8762 cellSelection : false,
8765 // Roo.Element - the tbody
8766 bodyEl: false, // <tbody> Roo.Element - thead element
8768 headEl: false, // <thead> Roo.Element - thead element
8770 container: false, // used by gridpanel...
8776 auto_hide_footer : false,
8778 view: false, // actually points to this..
8780 getAutoCreate : function()
8782 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8789 // this get's auto added by panel.Grid
8790 if (this.scrollBody) {
8791 cfg.cls += ' table-body-fixed';
8794 cfg.cls += ' table-striped';
8798 cfg.cls += ' table-hover';
8800 if (this.bordered) {
8801 cfg.cls += ' table-bordered';
8803 if (this.condensed) {
8804 cfg.cls += ' table-condensed';
8807 if (this.responsive) {
8808 cfg.cls += ' table-responsive';
8812 cfg.cls+= ' ' +this.cls;
8818 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8821 if(this.store || this.cm){
8822 if(this.headerShow){
8823 cfg.cn.push(this.renderHeader());
8826 cfg.cn.push(this.renderBody());
8828 if(this.footerShow){
8829 cfg.cn.push(this.renderFooter());
8831 // where does this come from?
8832 //cfg.cls+= ' TableGrid';
8835 return { cn : [ cfg ] };
8838 initEvents : function()
8840 if(!this.store || !this.cm){
8843 if (this.selModel) {
8844 this.selModel.initEvents();
8848 //Roo.log('initEvents with ds!!!!');
8850 this.bodyEl = this.el.select('tbody', true).first();
8851 this.headEl = this.el.select('thead', true).first();
8852 this.mainFoot = this.el.select('tfoot', true).first();
8857 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8858 e.on('click', this.sort, this);
8862 // why is this done????? = it breaks dialogs??
8863 //this.parent().el.setStyle('position', 'relative');
8867 this.footer.parentId = this.id;
8868 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8871 this.el.select('tfoot tr td').first().addClass('hide');
8876 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8879 this.store.on('load', this.onLoad, this);
8880 this.store.on('beforeload', this.onBeforeLoad, this);
8881 this.store.on('update', this.onUpdate, this);
8882 this.store.on('add', this.onAdd, this);
8883 this.store.on("clear", this.clear, this);
8885 this.el.on("contextmenu", this.onContextMenu, this);
8888 this.cm.on("headerchange", this.onHeaderChange, this);
8889 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8891 //?? does bodyEl get replaced on render?
8892 this.bodyEl.on("click", this.onClick, this);
8893 this.bodyEl.on("dblclick", this.onDblClick, this);
8894 this.bodyEl.on('scroll', this.onBodyScroll, this);
8896 // guessing mainbody will work - this relays usually caught by selmodel at present.
8897 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8899 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
8900 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
8905 // Compatibility with grid - we implement all the view features at present.
8906 getView : function()
8911 onContextMenu : function(e, t)
8913 this.processEvent("contextmenu", e);
8916 processEvent : function(name, e)
8918 if (name != 'touchstart' ) {
8919 this.fireEvent(name, e);
8922 var t = e.getTarget();
8924 var cell = Roo.get(t);
8930 if(cell.findParent('tfoot', false, true)){
8934 if(cell.findParent('thead', false, true)){
8936 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8937 cell = Roo.get(t).findParent('th', false, true);
8939 Roo.log("failed to find th in thead?");
8940 Roo.log(e.getTarget());
8945 var cellIndex = cell.dom.cellIndex;
8947 var ename = name == 'touchstart' ? 'click' : name;
8948 this.fireEvent("header" + ename, this, cellIndex, e);
8953 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8954 cell = Roo.get(t).findParent('td', false, true);
8956 Roo.log("failed to find th in tbody?");
8957 Roo.log(e.getTarget());
8962 var row = cell.findParent('tr', false, true);
8963 var cellIndex = cell.dom.cellIndex;
8964 var rowIndex = row.dom.rowIndex - 1;
8968 this.fireEvent("row" + name, this, rowIndex, e);
8972 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8978 onMouseover : function(e, el)
8980 var cell = Roo.get(el);
8986 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8987 cell = cell.findParent('td', false, true);
8990 var row = cell.findParent('tr', false, true);
8991 var cellIndex = cell.dom.cellIndex;
8992 var rowIndex = row.dom.rowIndex - 1; // start from 0
8994 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8998 onMouseout : function(e, el)
9000 var cell = Roo.get(el);
9006 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9007 cell = cell.findParent('td', false, true);
9010 var row = cell.findParent('tr', false, true);
9011 var cellIndex = cell.dom.cellIndex;
9012 var rowIndex = row.dom.rowIndex - 1; // start from 0
9014 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9018 onClick : function(e, el)
9020 var cell = Roo.get(el);
9022 if(!cell || (!this.cellSelection && !this.rowSelection)){
9026 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9027 cell = cell.findParent('td', false, true);
9030 if(!cell || typeof(cell) == 'undefined'){
9034 var row = cell.findParent('tr', false, true);
9036 if(!row || typeof(row) == 'undefined'){
9040 var cellIndex = cell.dom.cellIndex;
9041 var rowIndex = this.getRowIndex(row);
9043 // why??? - should these not be based on SelectionModel?
9044 //if(this.cellSelection){
9045 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9048 //if(this.rowSelection){
9049 this.fireEvent('rowclick', this, row, rowIndex, e);
9054 onDblClick : function(e,el)
9056 var cell = Roo.get(el);
9058 if(!cell || (!this.cellSelection && !this.rowSelection)){
9062 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9063 cell = cell.findParent('td', false, true);
9066 if(!cell || typeof(cell) == 'undefined'){
9070 var row = cell.findParent('tr', false, true);
9072 if(!row || typeof(row) == 'undefined'){
9076 var cellIndex = cell.dom.cellIndex;
9077 var rowIndex = this.getRowIndex(row);
9079 if(this.cellSelection){
9080 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9083 if(this.rowSelection){
9084 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9087 findRowIndex : function(el)
9089 var cell = Roo.get(el);
9093 var row = cell.findParent('tr', false, true);
9095 if(!row || typeof(row) == 'undefined'){
9098 return this.getRowIndex(row);
9100 sort : function(e,el)
9102 var col = Roo.get(el);
9104 if(!col.hasClass('sortable')){
9108 var sort = col.attr('sort');
9111 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9115 this.store.sortInfo = {field : sort, direction : dir};
9118 Roo.log("calling footer first");
9119 this.footer.onClick('first');
9122 this.store.load({ params : { start : 0 } });
9126 renderHeader : function()
9134 this.totalWidth = 0;
9136 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9138 var config = cm.config[i];
9142 cls : 'x-hcol-' + i,
9145 html: cm.getColumnHeader(i)
9148 var tooltip = cm.getColumnTooltip(i);
9150 c.tooltip = tooltip;
9156 if(typeof(config.sortable) != 'undefined' && config.sortable){
9158 c.html = '<i class="fa"></i>' + c.html;
9161 // could use BS4 hidden-..-down
9163 if(typeof(config.lgHeader) != 'undefined'){
9164 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9167 if(typeof(config.mdHeader) != 'undefined'){
9168 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9171 if(typeof(config.smHeader) != 'undefined'){
9172 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9175 if(typeof(config.xsHeader) != 'undefined'){
9176 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9183 if(typeof(config.tooltip) != 'undefined'){
9184 c.tooltip = config.tooltip;
9187 if(typeof(config.colspan) != 'undefined'){
9188 c.colspan = config.colspan;
9191 if(typeof(config.hidden) != 'undefined' && config.hidden){
9194 c.cls += ' d-block';
9197 if(typeof(config.dataIndex) != 'undefined'){
9198 c.sort = config.dataIndex;
9203 if(typeof(config.align) != 'undefined' && config.align.length){
9204 c.style += ' text-align:' + config.align + ';';
9207 if(typeof(config.width) != 'undefined'){
9208 c.style += ' width:' + config.width + 'px;';
9209 this.totalWidth += config.width;
9211 this.totalWidth += 100; // assume minimum of 100 per column?
9214 if(typeof(config.cls) != 'undefined'){
9215 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9218 ['xs','sm','md','lg'].map(function(size){
9220 if(typeof(config[size]) == 'undefined'){
9224 if (!config[size]) { // 0 = hidden
9225 // BS 4 '0' is treated as hide that column and below.
9226 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9230 c.cls += ' col-' + size + '-' + config[size] + (
9231 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9237 c.html +=' <span class="roo-hd-split"></span>';
9246 renderBody : function()
9256 colspan : this.cm.getColumnCount()
9266 renderFooter : function()
9276 colspan : this.cm.getColumnCount()
9290 // Roo.log('ds onload');
9295 var ds = this.store;
9297 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9298 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9299 if (_this.store.sortInfo) {
9301 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9302 e.select('i', true).addClass(['fa-arrow-up']);
9305 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9306 e.select('i', true).addClass(['fa-arrow-down']);
9311 var tbody = this.bodyEl;
9313 if(ds.getCount() > 0){
9314 ds.data.each(function(d,rowIndex){
9315 var row = this.renderRow(cm, ds, rowIndex);
9317 tbody.createChild(row);
9321 if(row.cellObjects.length){
9322 Roo.each(row.cellObjects, function(r){
9323 _this.renderCellObject(r);
9330 var tfoot = this.el.select('tfoot', true).first();
9332 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9334 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9336 var total = this.ds.getTotalCount();
9338 if(this.footer.pageSize < total){
9339 this.mainFoot.show();
9343 Roo.each(this.el.select('tbody td', true).elements, function(e){
9344 e.on('mouseover', _this.onMouseover, _this);
9347 Roo.each(this.el.select('tbody td', true).elements, function(e){
9348 e.on('mouseout', _this.onMouseout, _this);
9350 this.fireEvent('rowsrendered', this);
9356 onUpdate : function(ds,record)
9358 this.refreshRow(record);
9362 onRemove : function(ds, record, index, isUpdate){
9363 if(isUpdate !== true){
9364 this.fireEvent("beforerowremoved", this, index, record);
9366 var bt = this.bodyEl.dom;
9368 var rows = this.el.select('tbody > tr', true).elements;
9370 if(typeof(rows[index]) != 'undefined'){
9371 bt.removeChild(rows[index].dom);
9374 // if(bt.rows[index]){
9375 // bt.removeChild(bt.rows[index]);
9378 if(isUpdate !== true){
9379 //this.stripeRows(index);
9380 //this.syncRowHeights(index, index);
9382 this.fireEvent("rowremoved", this, index, record);
9386 onAdd : function(ds, records, rowIndex)
9388 //Roo.log('on Add called');
9389 // - note this does not handle multiple adding very well..
9390 var bt = this.bodyEl.dom;
9391 for (var i =0 ; i < records.length;i++) {
9392 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9393 //Roo.log(records[i]);
9394 //Roo.log(this.store.getAt(rowIndex+i));
9395 this.insertRow(this.store, rowIndex + i, false);
9402 refreshRow : function(record){
9403 var ds = this.store, index;
9404 if(typeof record == 'number'){
9406 record = ds.getAt(index);
9408 index = ds.indexOf(record);
9410 return; // should not happen - but seems to
9413 this.insertRow(ds, index, true);
9415 this.onRemove(ds, record, index+1, true);
9417 //this.syncRowHeights(index, index);
9419 this.fireEvent("rowupdated", this, index, record);
9422 onRowSelect : function(rowIndex){
9423 var row = this.getRowDom(rowIndex);
9424 row.addClass(['bg-info','info']);
9427 onRowDeselect : function(rowIndex){
9428 var row = this.getRowDom(rowIndex);
9429 row.removeClass(['bg-info','info']);
9432 * Focuses the specified row.
9433 * @param {Number} row The row index
9435 focusRow : function(row)
9437 //Roo.log('GridView.focusRow');
9438 var x = this.bodyEl.dom.scrollLeft;
9439 this.focusCell(row, 0, false);
9440 this.bodyEl.dom.scrollLeft = x;
9444 * Focuses the specified cell.
9445 * @param {Number} row The row index
9446 * @param {Number} col The column index
9447 * @param {Boolean} hscroll false to disable horizontal scrolling
9449 focusCell : function(row, col, hscroll)
9451 //Roo.log('GridView.focusCell');
9452 var el = this.ensureVisible(row, col, hscroll);
9453 // not sure what focusEL achives = it's a <a> pos relative
9454 //this.focusEl.alignTo(el, "tl-tl");
9456 // this.focusEl.focus();
9458 // this.focusEl.focus.defer(1, this.focusEl);
9463 * Scrolls the specified cell into view
9464 * @param {Number} row The row index
9465 * @param {Number} col The column index
9466 * @param {Boolean} hscroll false to disable horizontal scrolling
9468 ensureVisible : function(row, col, hscroll)
9470 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9471 //return null; //disable for testing.
9472 if(typeof row != "number"){
9475 if(row < 0 && row >= this.ds.getCount()){
9478 col = (col !== undefined ? col : 0);
9480 while(cm.isHidden(col)){
9484 var el = this.getCellDom(row, col);
9488 var c = this.bodyEl.dom;
9490 var ctop = parseInt(el.offsetTop, 10);
9491 var cleft = parseInt(el.offsetLeft, 10);
9492 var cbot = ctop + el.offsetHeight;
9493 var cright = cleft + el.offsetWidth;
9495 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9496 var ch = 0; //?? header is not withing the area?
9497 var stop = parseInt(c.scrollTop, 10);
9498 var sleft = parseInt(c.scrollLeft, 10);
9499 var sbot = stop + ch;
9500 var sright = sleft + c.clientWidth;
9502 Roo.log('GridView.ensureVisible:' +
9504 ' c.clientHeight:' + c.clientHeight +
9505 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9514 //Roo.log("set scrolltop to ctop DISABLE?");
9515 }else if(cbot > sbot){
9516 //Roo.log("set scrolltop to cbot-ch");
9517 c.scrollTop = cbot-ch;
9520 if(hscroll !== false){
9522 c.scrollLeft = cleft;
9523 }else if(cright > sright){
9524 c.scrollLeft = cright-c.clientWidth;
9532 insertRow : function(dm, rowIndex, isUpdate){
9535 this.fireEvent("beforerowsinserted", this, rowIndex);
9537 //var s = this.getScrollState();
9538 var row = this.renderRow(this.cm, this.store, rowIndex);
9539 // insert before rowIndex..
9540 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9544 if(row.cellObjects.length){
9545 Roo.each(row.cellObjects, function(r){
9546 _this.renderCellObject(r);
9551 this.fireEvent("rowsinserted", this, rowIndex);
9552 //this.syncRowHeights(firstRow, lastRow);
9553 //this.stripeRows(firstRow);
9560 getRowDom : function(rowIndex)
9562 var rows = this.el.select('tbody > tr', true).elements;
9564 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9567 getCellDom : function(rowIndex, colIndex)
9569 var row = this.getRowDom(rowIndex);
9570 if (row === false) {
9573 var cols = row.select('td', true).elements;
9574 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9578 // returns the object tree for a tr..
9581 renderRow : function(cm, ds, rowIndex)
9583 var d = ds.getAt(rowIndex);
9587 cls : 'x-row-' + rowIndex,
9591 var cellObjects = [];
9593 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9594 var config = cm.config[i];
9596 var renderer = cm.getRenderer(i);
9600 if(typeof(renderer) !== 'undefined'){
9601 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9603 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9604 // and are rendered into the cells after the row is rendered - using the id for the element.
9606 if(typeof(value) === 'object'){
9616 rowIndex : rowIndex,
9621 this.fireEvent('rowclass', this, rowcfg);
9625 // this might end up displaying HTML?
9626 // this is too messy... - better to only do it on columsn you know are going to be too long
9627 //tooltip : (typeof(value) === 'object') ? '' : value,
9628 cls : rowcfg.rowClass + ' x-col-' + i,
9630 html: (typeof(value) === 'object') ? '' : value
9637 if(typeof(config.colspan) != 'undefined'){
9638 td.colspan = config.colspan;
9641 if(typeof(config.hidden) != 'undefined' && config.hidden){
9642 td.cls += ' d-none';
9644 td.cls += ' d-block';
9647 if(typeof(config.align) != 'undefined' && config.align.length){
9648 td.style += ' text-align:' + config.align + ';';
9650 if(typeof(config.valign) != 'undefined' && config.valign.length){
9651 td.style += ' vertical-align:' + config.valign + ';';
9654 if(typeof(config.width) != 'undefined'){
9655 td.style += ' width:' + config.width + 'px;';
9658 if(typeof(config.cursor) != 'undefined'){
9659 td.style += ' cursor:' + config.cursor + ';';
9662 if(typeof(config.cls) != 'undefined'){
9663 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9666 ['xs','sm','md','lg'].map(function(size){
9668 if(typeof(config[size]) == 'undefined'){
9674 if (!config[size]) { // 0 = hidden
9675 // BS 4 '0' is treated as hide that column and below.
9676 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9680 td.cls += ' col-' + size + '-' + config[size] + (
9681 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9691 row.cellObjects = cellObjects;
9699 onBeforeLoad : function()
9708 this.el.select('tbody', true).first().dom.innerHTML = '';
9711 * Show or hide a row.
9712 * @param {Number} rowIndex to show or hide
9713 * @param {Boolean} state hide
9715 setRowVisibility : function(rowIndex, state)
9717 var bt = this.bodyEl.dom;
9719 var rows = this.el.select('tbody > tr', true).elements;
9721 if(typeof(rows[rowIndex]) == 'undefined'){
9724 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9729 getSelectionModel : function(){
9731 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9733 return this.selModel;
9736 * Render the Roo.bootstrap object from renderder
9738 renderCellObject : function(r)
9742 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9744 var t = r.cfg.render(r.container);
9747 Roo.each(r.cfg.cn, function(c){
9749 container: t.getChildContainer(),
9752 _this.renderCellObject(child);
9757 getRowIndex : function(row)
9761 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9772 * Returns the grid's underlying element = used by panel.Grid
9773 * @return {Element} The element
9775 getGridEl : function(){
9779 * Forces a resize - used by panel.Grid
9780 * @return {Element} The element
9782 autoSize : function()
9784 //var ctr = Roo.get(this.container.dom.parentElement);
9785 var ctr = Roo.get(this.el.dom);
9787 var thd = this.getGridEl().select('thead',true).first();
9788 var tbd = this.getGridEl().select('tbody', true).first();
9789 var tfd = this.getGridEl().select('tfoot', true).first();
9791 var cw = ctr.getWidth();
9792 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9796 tbd.setWidth(ctr.getWidth());
9797 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9798 // this needs fixing for various usage - currently only hydra job advers I think..
9800 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9802 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9805 cw = Math.max(cw, this.totalWidth);
9806 this.getGridEl().select('tbody tr',true).setWidth(cw);
9808 // resize 'expandable coloumn?
9810 return; // we doe not have a view in this design..
9813 onBodyScroll: function()
9815 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9817 this.headEl.setStyle({
9818 'position' : 'relative',
9819 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
9825 var scrollHeight = this.bodyEl.dom.scrollHeight;
9827 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
9829 var height = this.bodyEl.getHeight();
9831 if(scrollHeight - height == scrollTop) {
9833 var total = this.ds.getTotalCount();
9835 if(this.footer.cursor + this.footer.pageSize < total){
9837 this.footer.ds.load({
9839 start : this.footer.cursor + this.footer.pageSize,
9840 limit : this.footer.pageSize
9850 onHeaderChange : function()
9852 var header = this.renderHeader();
9853 var table = this.el.select('table', true).first();
9855 this.headEl.remove();
9856 this.headEl = table.createChild(header, this.bodyEl, false);
9858 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9859 e.on('click', this.sort, this);
9862 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9863 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9868 onHiddenChange : function(colModel, colIndex, hidden)
9870 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9871 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9873 //this.CSS.updateRule(thSelector, "display", "");
9874 var cols = this.headEl.select('th', true).elements;
9875 if (typeof(cols[colIndex]) != 'undefined') {
9876 cols[colIndex].removeClass(['d-none', 'd-block']);
9877 cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
9879 this.CSS.updateRule(tdSelector, "display", "");
9882 // this.CSS.updateRule(thSelector, "display", "none");
9883 this.CSS.updateRule(tdSelector, "display", "none");
9886 this.onHeaderChange();
9890 setColumnWidth: function(col_index, width)
9892 // width = "md-2 xs-2..."
9893 if(!this.colModel.config[col_index]) {
9897 var w = width.split(" ");
9899 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9901 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9904 for(var j = 0; j < w.length; j++) {
9910 var size_cls = w[j].split("-");
9912 if(!Number.isInteger(size_cls[1] * 1)) {
9916 if(!this.colModel.config[col_index][size_cls[0]]) {
9920 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9924 h_row[0].classList.replace(
9925 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9926 "col-"+size_cls[0]+"-"+size_cls[1]
9929 for(var i = 0; i < rows.length; i++) {
9931 var size_cls = w[j].split("-");
9933 if(!Number.isInteger(size_cls[1] * 1)) {
9937 if(!this.colModel.config[col_index][size_cls[0]]) {
9941 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9945 rows[i].classList.replace(
9946 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9947 "col-"+size_cls[0]+"-"+size_cls[1]
9951 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9961 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9962 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9970 * @class Roo.bootstrap.TableCell
9971 * @extends Roo.bootstrap.Component
9972 * Bootstrap TableCell class
9973 * @cfg {String} html cell contain text
9974 * @cfg {String} cls cell class
9975 * @cfg {String} tag cell tag (td|th) default td
9976 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9977 * @cfg {String} align Aligns the content in a cell
9978 * @cfg {String} axis Categorizes cells
9979 * @cfg {String} bgcolor Specifies the background color of a cell
9980 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9981 * @cfg {Number} colspan Specifies the number of columns a cell should span
9982 * @cfg {String} headers Specifies one or more header cells a cell is related to
9983 * @cfg {Number} height Sets the height of a cell
9984 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9985 * @cfg {Number} rowspan Sets the number of rows a cell should span
9986 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9987 * @cfg {String} valign Vertical aligns the content in a cell
9988 * @cfg {Number} width Specifies the width of a cell
9991 * Create a new TableCell
9992 * @param {Object} config The config object
9995 Roo.bootstrap.TableCell = function(config){
9996 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9999 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10019 getAutoCreate : function(){
10020 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10027 cfg.tag = this.tag;
10040 cfg.align=this.align
10045 if (this.bgcolor) {
10046 cfg.bgcolor=this.bgcolor
10048 if (this.charoff) {
10049 cfg.charoff=this.charoff
10051 if (this.colspan) {
10052 cfg.colspan=this.colspan
10054 if (this.headers) {
10055 cfg.headers=this.headers
10058 cfg.height=this.height
10061 cfg.nowrap=this.nowrap
10063 if (this.rowspan) {
10064 cfg.rowspan=this.rowspan
10067 cfg.scope=this.scope
10070 cfg.valign=this.valign
10073 cfg.width=this.width
10092 * @class Roo.bootstrap.TableRow
10093 * @extends Roo.bootstrap.Component
10094 * Bootstrap TableRow class
10095 * @cfg {String} cls row class
10096 * @cfg {String} align Aligns the content in a table row
10097 * @cfg {String} bgcolor Specifies a background color for a table row
10098 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10099 * @cfg {String} valign Vertical aligns the content in a table row
10102 * Create a new TableRow
10103 * @param {Object} config The config object
10106 Roo.bootstrap.TableRow = function(config){
10107 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10110 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10118 getAutoCreate : function(){
10119 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10126 cfg.cls = this.cls;
10129 cfg.align = this.align;
10132 cfg.bgcolor = this.bgcolor;
10135 cfg.charoff = this.charoff;
10138 cfg.valign = this.valign;
10156 * @class Roo.bootstrap.TableBody
10157 * @extends Roo.bootstrap.Component
10158 * Bootstrap TableBody class
10159 * @cfg {String} cls element class
10160 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10161 * @cfg {String} align Aligns the content inside the element
10162 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10163 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10166 * Create a new TableBody
10167 * @param {Object} config The config object
10170 Roo.bootstrap.TableBody = function(config){
10171 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10174 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10182 getAutoCreate : function(){
10183 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10193 cfg.tag = this.tag;
10197 cfg.align = this.align;
10200 cfg.charoff = this.charoff;
10203 cfg.valign = this.valign;
10210 // initEvents : function()
10213 // if(!this.store){
10217 // this.store = Roo.factory(this.store, Roo.data);
10218 // this.store.on('load', this.onLoad, this);
10220 // this.store.load();
10224 // onLoad: function ()
10226 // this.fireEvent('load', this);
10236 * Ext JS Library 1.1.1
10237 * Copyright(c) 2006-2007, Ext JS, LLC.
10239 * Originally Released Under LGPL - original licence link has changed is not relivant.
10242 * <script type="text/javascript">
10245 // as we use this in bootstrap.
10246 Roo.namespace('Roo.form');
10248 * @class Roo.form.Action
10249 * Internal Class used to handle form actions
10251 * @param {Roo.form.BasicForm} el The form element or its id
10252 * @param {Object} config Configuration options
10257 // define the action interface
10258 Roo.form.Action = function(form, options){
10260 this.options = options || {};
10263 * Client Validation Failed
10266 Roo.form.Action.CLIENT_INVALID = 'client';
10268 * Server Validation Failed
10271 Roo.form.Action.SERVER_INVALID = 'server';
10273 * Connect to Server Failed
10276 Roo.form.Action.CONNECT_FAILURE = 'connect';
10278 * Reading Data from Server Failed
10281 Roo.form.Action.LOAD_FAILURE = 'load';
10283 Roo.form.Action.prototype = {
10285 failureType : undefined,
10286 response : undefined,
10287 result : undefined,
10289 // interface method
10290 run : function(options){
10294 // interface method
10295 success : function(response){
10299 // interface method
10300 handleResponse : function(response){
10304 // default connection failure
10305 failure : function(response){
10307 this.response = response;
10308 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10309 this.form.afterAction(this, false);
10312 processResponse : function(response){
10313 this.response = response;
10314 if(!response.responseText){
10317 this.result = this.handleResponse(response);
10318 return this.result;
10321 // utility functions used internally
10322 getUrl : function(appendParams){
10323 var url = this.options.url || this.form.url || this.form.el.dom.action;
10325 var p = this.getParams();
10327 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10333 getMethod : function(){
10334 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10337 getParams : function(){
10338 var bp = this.form.baseParams;
10339 var p = this.options.params;
10341 if(typeof p == "object"){
10342 p = Roo.urlEncode(Roo.applyIf(p, bp));
10343 }else if(typeof p == 'string' && bp){
10344 p += '&' + Roo.urlEncode(bp);
10347 p = Roo.urlEncode(bp);
10352 createCallback : function(){
10354 success: this.success,
10355 failure: this.failure,
10357 timeout: (this.form.timeout*1000),
10358 upload: this.form.fileUpload ? this.success : undefined
10363 Roo.form.Action.Submit = function(form, options){
10364 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10367 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10370 haveProgress : false,
10371 uploadComplete : false,
10373 // uploadProgress indicator.
10374 uploadProgress : function()
10376 if (!this.form.progressUrl) {
10380 if (!this.haveProgress) {
10381 Roo.MessageBox.progress("Uploading", "Uploading");
10383 if (this.uploadComplete) {
10384 Roo.MessageBox.hide();
10388 this.haveProgress = true;
10390 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10392 var c = new Roo.data.Connection();
10394 url : this.form.progressUrl,
10399 success : function(req){
10400 //console.log(data);
10404 rdata = Roo.decode(req.responseText)
10406 Roo.log("Invalid data from server..");
10410 if (!rdata || !rdata.success) {
10412 Roo.MessageBox.alert(Roo.encode(rdata));
10415 var data = rdata.data;
10417 if (this.uploadComplete) {
10418 Roo.MessageBox.hide();
10423 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10424 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10427 this.uploadProgress.defer(2000,this);
10430 failure: function(data) {
10431 Roo.log('progress url failed ');
10442 // run get Values on the form, so it syncs any secondary forms.
10443 this.form.getValues();
10445 var o = this.options;
10446 var method = this.getMethod();
10447 var isPost = method == 'POST';
10448 if(o.clientValidation === false || this.form.isValid()){
10450 if (this.form.progressUrl) {
10451 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10452 (new Date() * 1) + '' + Math.random());
10457 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10458 form:this.form.el.dom,
10459 url:this.getUrl(!isPost),
10461 params:isPost ? this.getParams() : null,
10462 isUpload: this.form.fileUpload,
10463 formData : this.form.formData
10466 this.uploadProgress();
10468 }else if (o.clientValidation !== false){ // client validation failed
10469 this.failureType = Roo.form.Action.CLIENT_INVALID;
10470 this.form.afterAction(this, false);
10474 success : function(response)
10476 this.uploadComplete= true;
10477 if (this.haveProgress) {
10478 Roo.MessageBox.hide();
10482 var result = this.processResponse(response);
10483 if(result === true || result.success){
10484 this.form.afterAction(this, true);
10488 this.form.markInvalid(result.errors);
10489 this.failureType = Roo.form.Action.SERVER_INVALID;
10491 this.form.afterAction(this, false);
10493 failure : function(response)
10495 this.uploadComplete= true;
10496 if (this.haveProgress) {
10497 Roo.MessageBox.hide();
10500 this.response = response;
10501 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10502 this.form.afterAction(this, false);
10505 handleResponse : function(response){
10506 if(this.form.errorReader){
10507 var rs = this.form.errorReader.read(response);
10510 for(var i = 0, len = rs.records.length; i < len; i++) {
10511 var r = rs.records[i];
10512 errors[i] = r.data;
10515 if(errors.length < 1){
10519 success : rs.success,
10525 ret = Roo.decode(response.responseText);
10529 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10539 Roo.form.Action.Load = function(form, options){
10540 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10541 this.reader = this.form.reader;
10544 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10549 Roo.Ajax.request(Roo.apply(
10550 this.createCallback(), {
10551 method:this.getMethod(),
10552 url:this.getUrl(false),
10553 params:this.getParams()
10557 success : function(response){
10559 var result = this.processResponse(response);
10560 if(result === true || !result.success || !result.data){
10561 this.failureType = Roo.form.Action.LOAD_FAILURE;
10562 this.form.afterAction(this, false);
10565 this.form.clearInvalid();
10566 this.form.setValues(result.data);
10567 this.form.afterAction(this, true);
10570 handleResponse : function(response){
10571 if(this.form.reader){
10572 var rs = this.form.reader.read(response);
10573 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10575 success : rs.success,
10579 return Roo.decode(response.responseText);
10583 Roo.form.Action.ACTION_TYPES = {
10584 'load' : Roo.form.Action.Load,
10585 'submit' : Roo.form.Action.Submit
10594 * @class Roo.bootstrap.Form
10595 * @extends Roo.bootstrap.Component
10596 * Bootstrap Form class
10597 * @cfg {String} method GET | POST (default POST)
10598 * @cfg {String} labelAlign top | left (default top)
10599 * @cfg {String} align left | right - for navbars
10600 * @cfg {Boolean} loadMask load mask when submit (default true)
10604 * Create a new Form
10605 * @param {Object} config The config object
10609 Roo.bootstrap.Form = function(config){
10611 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10613 Roo.bootstrap.Form.popover.apply();
10617 * @event clientvalidation
10618 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10619 * @param {Form} this
10620 * @param {Boolean} valid true if the form has passed client-side validation
10622 clientvalidation: true,
10624 * @event beforeaction
10625 * Fires before any action is performed. Return false to cancel the action.
10626 * @param {Form} this
10627 * @param {Action} action The action to be performed
10629 beforeaction: true,
10631 * @event actionfailed
10632 * Fires when an action fails.
10633 * @param {Form} this
10634 * @param {Action} action The action that failed
10636 actionfailed : true,
10638 * @event actioncomplete
10639 * Fires when an action is completed.
10640 * @param {Form} this
10641 * @param {Action} action The action that completed
10643 actioncomplete : true
10647 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10650 * @cfg {String} method
10651 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10655 * @cfg {String} url
10656 * The URL to use for form actions if one isn't supplied in the action options.
10659 * @cfg {Boolean} fileUpload
10660 * Set to true if this form is a file upload.
10664 * @cfg {Object} baseParams
10665 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10669 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10673 * @cfg {Sting} align (left|right) for navbar forms
10678 activeAction : null,
10681 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10682 * element by passing it or its id or mask the form itself by passing in true.
10685 waitMsgTarget : false,
10690 * @cfg {Boolean} errorMask (true|false) default false
10695 * @cfg {Number} maskOffset Default 100
10700 * @cfg {Boolean} maskBody
10704 getAutoCreate : function(){
10708 method : this.method || 'POST',
10709 id : this.id || Roo.id(),
10712 if (this.parent().xtype.match(/^Nav/)) {
10713 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10717 if (this.labelAlign == 'left' ) {
10718 cfg.cls += ' form-horizontal';
10724 initEvents : function()
10726 this.el.on('submit', this.onSubmit, this);
10727 // this was added as random key presses on the form where triggering form submit.
10728 this.el.on('keypress', function(e) {
10729 if (e.getCharCode() != 13) {
10732 // we might need to allow it for textareas.. and some other items.
10733 // check e.getTarget().
10735 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10739 Roo.log("keypress blocked");
10741 e.preventDefault();
10747 onSubmit : function(e){
10752 * Returns true if client-side validation on the form is successful.
10755 isValid : function(){
10756 var items = this.getItems();
10758 var target = false;
10760 items.each(function(f){
10766 Roo.log('invalid field: ' + f.name);
10770 if(!target && f.el.isVisible(true)){
10776 if(this.errorMask && !valid){
10777 Roo.bootstrap.Form.popover.mask(this, target);
10784 * Returns true if any fields in this form have changed since their original load.
10787 isDirty : function(){
10789 var items = this.getItems();
10790 items.each(function(f){
10800 * Performs a predefined action (submit or load) or custom actions you define on this form.
10801 * @param {String} actionName The name of the action type
10802 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10803 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10804 * accept other config options):
10806 Property Type Description
10807 ---------------- --------------- ----------------------------------------------------------------------------------
10808 url String The url for the action (defaults to the form's url)
10809 method String The form method to use (defaults to the form's method, or POST if not defined)
10810 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10811 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10812 validate the form on the client (defaults to false)
10814 * @return {BasicForm} this
10816 doAction : function(action, options){
10817 if(typeof action == 'string'){
10818 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10820 if(this.fireEvent('beforeaction', this, action) !== false){
10821 this.beforeAction(action);
10822 action.run.defer(100, action);
10828 beforeAction : function(action){
10829 var o = action.options;
10834 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10836 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10839 // not really supported yet.. ??
10841 //if(this.waitMsgTarget === true){
10842 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10843 //}else if(this.waitMsgTarget){
10844 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10845 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10847 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10853 afterAction : function(action, success){
10854 this.activeAction = null;
10855 var o = action.options;
10860 Roo.get(document.body).unmask();
10866 //if(this.waitMsgTarget === true){
10867 // this.el.unmask();
10868 //}else if(this.waitMsgTarget){
10869 // this.waitMsgTarget.unmask();
10871 // Roo.MessageBox.updateProgress(1);
10872 // Roo.MessageBox.hide();
10879 Roo.callback(o.success, o.scope, [this, action]);
10880 this.fireEvent('actioncomplete', this, action);
10884 // failure condition..
10885 // we have a scenario where updates need confirming.
10886 // eg. if a locking scenario exists..
10887 // we look for { errors : { needs_confirm : true }} in the response.
10889 (typeof(action.result) != 'undefined') &&
10890 (typeof(action.result.errors) != 'undefined') &&
10891 (typeof(action.result.errors.needs_confirm) != 'undefined')
10894 Roo.log("not supported yet");
10897 Roo.MessageBox.confirm(
10898 "Change requires confirmation",
10899 action.result.errorMsg,
10904 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10914 Roo.callback(o.failure, o.scope, [this, action]);
10915 // show an error message if no failed handler is set..
10916 if (!this.hasListener('actionfailed')) {
10917 Roo.log("need to add dialog support");
10919 Roo.MessageBox.alert("Error",
10920 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10921 action.result.errorMsg :
10922 "Saving Failed, please check your entries or try again"
10927 this.fireEvent('actionfailed', this, action);
10932 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10933 * @param {String} id The value to search for
10936 findField : function(id){
10937 var items = this.getItems();
10938 var field = items.get(id);
10940 items.each(function(f){
10941 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10948 return field || null;
10951 * Mark fields in this form invalid in bulk.
10952 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10953 * @return {BasicForm} this
10955 markInvalid : function(errors){
10956 if(errors instanceof Array){
10957 for(var i = 0, len = errors.length; i < len; i++){
10958 var fieldError = errors[i];
10959 var f = this.findField(fieldError.id);
10961 f.markInvalid(fieldError.msg);
10967 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10968 field.markInvalid(errors[id]);
10972 //Roo.each(this.childForms || [], function (f) {
10973 // f.markInvalid(errors);
10980 * Set values for fields in this form in bulk.
10981 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10982 * @return {BasicForm} this
10984 setValues : function(values){
10985 if(values instanceof Array){ // array of objects
10986 for(var i = 0, len = values.length; i < len; i++){
10988 var f = this.findField(v.id);
10990 f.setValue(v.value);
10991 if(this.trackResetOnLoad){
10992 f.originalValue = f.getValue();
10996 }else{ // object hash
10999 if(typeof values[id] != 'function' && (field = this.findField(id))){
11001 if (field.setFromData &&
11002 field.valueField &&
11003 field.displayField &&
11004 // combos' with local stores can
11005 // be queried via setValue()
11006 // to set their value..
11007 (field.store && !field.store.isLocal)
11011 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11012 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11013 field.setFromData(sd);
11015 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11017 field.setFromData(values);
11020 field.setValue(values[id]);
11024 if(this.trackResetOnLoad){
11025 field.originalValue = field.getValue();
11031 //Roo.each(this.childForms || [], function (f) {
11032 // f.setValues(values);
11039 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11040 * they are returned as an array.
11041 * @param {Boolean} asString
11044 getValues : function(asString){
11045 //if (this.childForms) {
11046 // copy values from the child forms
11047 // Roo.each(this.childForms, function (f) {
11048 // this.setValues(f.getValues());
11054 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11055 if(asString === true){
11058 return Roo.urlDecode(fs);
11062 * Returns the fields in this form as an object with key/value pairs.
11063 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11066 getFieldValues : function(with_hidden)
11068 var items = this.getItems();
11070 items.each(function(f){
11072 if (!f.getName()) {
11076 var v = f.getValue();
11078 if (f.inputType =='radio') {
11079 if (typeof(ret[f.getName()]) == 'undefined') {
11080 ret[f.getName()] = ''; // empty..
11083 if (!f.el.dom.checked) {
11087 v = f.el.dom.value;
11091 if(f.xtype == 'MoneyField'){
11092 ret[f.currencyName] = f.getCurrency();
11095 // not sure if this supported any more..
11096 if ((typeof(v) == 'object') && f.getRawValue) {
11097 v = f.getRawValue() ; // dates..
11099 // combo boxes where name != hiddenName...
11100 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11101 ret[f.name] = f.getRawValue();
11103 ret[f.getName()] = v;
11110 * Clears all invalid messages in this form.
11111 * @return {BasicForm} this
11113 clearInvalid : function(){
11114 var items = this.getItems();
11116 items.each(function(f){
11124 * Resets this form.
11125 * @return {BasicForm} this
11127 reset : function(){
11128 var items = this.getItems();
11129 items.each(function(f){
11133 Roo.each(this.childForms || [], function (f) {
11141 getItems : function()
11143 var r=new Roo.util.MixedCollection(false, function(o){
11144 return o.id || (o.id = Roo.id());
11146 var iter = function(el) {
11153 Roo.each(el.items,function(e) {
11162 hideFields : function(items)
11164 Roo.each(items, function(i){
11166 var f = this.findField(i);
11177 showFields : function(items)
11179 Roo.each(items, function(i){
11181 var f = this.findField(i);
11194 Roo.apply(Roo.bootstrap.Form, {
11210 intervalID : false,
11216 if(this.isApplied){
11221 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11222 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11223 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11224 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11227 this.maskEl.top.enableDisplayMode("block");
11228 this.maskEl.left.enableDisplayMode("block");
11229 this.maskEl.bottom.enableDisplayMode("block");
11230 this.maskEl.right.enableDisplayMode("block");
11232 this.toolTip = new Roo.bootstrap.Tooltip({
11233 cls : 'roo-form-error-popover',
11235 'left' : ['r-l', [-2,0], 'right'],
11236 'right' : ['l-r', [2,0], 'left'],
11237 'bottom' : ['tl-bl', [0,2], 'top'],
11238 'top' : [ 'bl-tl', [0,-2], 'bottom']
11242 this.toolTip.render(Roo.get(document.body));
11244 this.toolTip.el.enableDisplayMode("block");
11246 Roo.get(document.body).on('click', function(){
11250 Roo.get(document.body).on('touchstart', function(){
11254 this.isApplied = true
11257 mask : function(form, target)
11261 this.target = target;
11263 if(!this.form.errorMask || !target.el){
11267 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11269 Roo.log(scrollable);
11271 var ot = this.target.el.calcOffsetsTo(scrollable);
11273 var scrollTo = ot[1] - this.form.maskOffset;
11275 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11277 scrollable.scrollTo('top', scrollTo);
11279 var box = this.target.el.getBox();
11281 var zIndex = Roo.bootstrap.Modal.zIndex++;
11284 this.maskEl.top.setStyle('position', 'absolute');
11285 this.maskEl.top.setStyle('z-index', zIndex);
11286 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11287 this.maskEl.top.setLeft(0);
11288 this.maskEl.top.setTop(0);
11289 this.maskEl.top.show();
11291 this.maskEl.left.setStyle('position', 'absolute');
11292 this.maskEl.left.setStyle('z-index', zIndex);
11293 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11294 this.maskEl.left.setLeft(0);
11295 this.maskEl.left.setTop(box.y - this.padding);
11296 this.maskEl.left.show();
11298 this.maskEl.bottom.setStyle('position', 'absolute');
11299 this.maskEl.bottom.setStyle('z-index', zIndex);
11300 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11301 this.maskEl.bottom.setLeft(0);
11302 this.maskEl.bottom.setTop(box.bottom + this.padding);
11303 this.maskEl.bottom.show();
11305 this.maskEl.right.setStyle('position', 'absolute');
11306 this.maskEl.right.setStyle('z-index', zIndex);
11307 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11308 this.maskEl.right.setLeft(box.right + this.padding);
11309 this.maskEl.right.setTop(box.y - this.padding);
11310 this.maskEl.right.show();
11312 this.toolTip.bindEl = this.target.el;
11314 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11316 var tip = this.target.blankText;
11318 if(this.target.getValue() !== '' ) {
11320 if (this.target.invalidText.length) {
11321 tip = this.target.invalidText;
11322 } else if (this.target.regexText.length){
11323 tip = this.target.regexText;
11327 this.toolTip.show(tip);
11329 this.intervalID = window.setInterval(function() {
11330 Roo.bootstrap.Form.popover.unmask();
11333 window.onwheel = function(){ return false;};
11335 (function(){ this.isMasked = true; }).defer(500, this);
11339 unmask : function()
11341 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11345 this.maskEl.top.setStyle('position', 'absolute');
11346 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11347 this.maskEl.top.hide();
11349 this.maskEl.left.setStyle('position', 'absolute');
11350 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11351 this.maskEl.left.hide();
11353 this.maskEl.bottom.setStyle('position', 'absolute');
11354 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11355 this.maskEl.bottom.hide();
11357 this.maskEl.right.setStyle('position', 'absolute');
11358 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11359 this.maskEl.right.hide();
11361 this.toolTip.hide();
11363 this.toolTip.el.hide();
11365 window.onwheel = function(){ return true;};
11367 if(this.intervalID){
11368 window.clearInterval(this.intervalID);
11369 this.intervalID = false;
11372 this.isMasked = false;
11382 * Ext JS Library 1.1.1
11383 * Copyright(c) 2006-2007, Ext JS, LLC.
11385 * Originally Released Under LGPL - original licence link has changed is not relivant.
11388 * <script type="text/javascript">
11391 * @class Roo.form.VTypes
11392 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11395 Roo.form.VTypes = function(){
11396 // closure these in so they are only created once.
11397 var alpha = /^[a-zA-Z_]+$/;
11398 var alphanum = /^[a-zA-Z0-9_]+$/;
11399 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11400 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11402 // All these messages and functions are configurable
11405 * The function used to validate email addresses
11406 * @param {String} value The email address
11408 'email' : function(v){
11409 return email.test(v);
11412 * The error text to display when the email validation function returns false
11415 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11417 * The keystroke filter mask to be applied on email input
11420 'emailMask' : /[a-z0-9_\.\-@]/i,
11423 * The function used to validate URLs
11424 * @param {String} value The URL
11426 'url' : function(v){
11427 return url.test(v);
11430 * The error text to display when the url validation function returns false
11433 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11436 * The function used to validate alpha values
11437 * @param {String} value The value
11439 'alpha' : function(v){
11440 return alpha.test(v);
11443 * The error text to display when the alpha validation function returns false
11446 'alphaText' : 'This field should only contain letters and _',
11448 * The keystroke filter mask to be applied on alpha input
11451 'alphaMask' : /[a-z_]/i,
11454 * The function used to validate alphanumeric values
11455 * @param {String} value The value
11457 'alphanum' : function(v){
11458 return alphanum.test(v);
11461 * The error text to display when the alphanumeric validation function returns false
11464 'alphanumText' : 'This field should only contain letters, numbers and _',
11466 * The keystroke filter mask to be applied on alphanumeric input
11469 'alphanumMask' : /[a-z0-9_]/i
11479 * @class Roo.bootstrap.Input
11480 * @extends Roo.bootstrap.Component
11481 * Bootstrap Input class
11482 * @cfg {Boolean} disabled is it disabled
11483 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11484 * @cfg {String} name name of the input
11485 * @cfg {string} fieldLabel - the label associated
11486 * @cfg {string} placeholder - placeholder to put in text.
11487 * @cfg {string} before - input group add on before
11488 * @cfg {string} after - input group add on after
11489 * @cfg {string} size - (lg|sm) or leave empty..
11490 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11491 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11492 * @cfg {Number} md colspan out of 12 for computer-sized screens
11493 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11494 * @cfg {string} value default value of the input
11495 * @cfg {Number} labelWidth set the width of label
11496 * @cfg {Number} labellg set the width of label (1-12)
11497 * @cfg {Number} labelmd set the width of label (1-12)
11498 * @cfg {Number} labelsm set the width of label (1-12)
11499 * @cfg {Number} labelxs set the width of label (1-12)
11500 * @cfg {String} labelAlign (top|left)
11501 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11502 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11503 * @cfg {String} indicatorpos (left|right) default left
11504 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11505 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11506 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11508 * @cfg {String} align (left|center|right) Default left
11509 * @cfg {Boolean} forceFeedback (true|false) Default false
11512 * Create a new Input
11513 * @param {Object} config The config object
11516 Roo.bootstrap.Input = function(config){
11518 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11523 * Fires when this field receives input focus.
11524 * @param {Roo.form.Field} this
11529 * Fires when this field loses input focus.
11530 * @param {Roo.form.Field} this
11534 * @event specialkey
11535 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11536 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11537 * @param {Roo.form.Field} this
11538 * @param {Roo.EventObject} e The event object
11543 * Fires just before the field blurs if the field value has changed.
11544 * @param {Roo.form.Field} this
11545 * @param {Mixed} newValue The new value
11546 * @param {Mixed} oldValue The original value
11551 * Fires after the field has been marked as invalid.
11552 * @param {Roo.form.Field} this
11553 * @param {String} msg The validation message
11558 * Fires after the field has been validated with no errors.
11559 * @param {Roo.form.Field} this
11564 * Fires after the key up
11565 * @param {Roo.form.Field} this
11566 * @param {Roo.EventObject} e The event Object
11571 * Fires after the user pastes into input
11572 * @param {Roo.form.Field} this
11573 * @param {Roo.EventObject} e The event Object
11579 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11581 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11582 automatic validation (defaults to "keyup").
11584 validationEvent : "keyup",
11586 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11588 validateOnBlur : true,
11590 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11592 validationDelay : 250,
11594 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11596 focusClass : "x-form-focus", // not needed???
11600 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11602 invalidClass : "has-warning",
11605 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11607 validClass : "has-success",
11610 * @cfg {Boolean} hasFeedback (true|false) default true
11612 hasFeedback : true,
11615 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11617 invalidFeedbackClass : "glyphicon-warning-sign",
11620 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11622 validFeedbackClass : "glyphicon-ok",
11625 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11627 selectOnFocus : false,
11630 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11634 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11639 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11641 disableKeyFilter : false,
11644 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11648 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11652 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11654 blankText : "Please complete this mandatory field",
11657 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11661 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11663 maxLength : Number.MAX_VALUE,
11665 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11667 minLengthText : "The minimum length for this field is {0}",
11669 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11671 maxLengthText : "The maximum length for this field is {0}",
11675 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11676 * If available, this function will be called only after the basic validators all return true, and will be passed the
11677 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11681 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11682 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11683 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11687 * @cfg {String} regexText -- Depricated - use Invalid Text
11692 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11698 autocomplete: false,
11702 inputType : 'text',
11705 placeholder: false,
11710 preventMark: false,
11711 isFormField : true,
11714 labelAlign : false,
11717 formatedValue : false,
11718 forceFeedback : false,
11720 indicatorpos : 'left',
11730 parentLabelAlign : function()
11733 while (parent.parent()) {
11734 parent = parent.parent();
11735 if (typeof(parent.labelAlign) !='undefined') {
11736 return parent.labelAlign;
11743 getAutoCreate : function()
11745 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11751 if(this.inputType != 'hidden'){
11752 cfg.cls = 'form-group' //input-group
11758 type : this.inputType,
11759 value : this.value,
11760 cls : 'form-control',
11761 placeholder : this.placeholder || '',
11762 autocomplete : this.autocomplete || 'new-password'
11764 if (this.inputType == 'file') {
11765 input.style = 'overflow:hidden'; // why not in CSS?
11768 if(this.capture.length){
11769 input.capture = this.capture;
11772 if(this.accept.length){
11773 input.accept = this.accept + "/*";
11777 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11780 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11781 input.maxLength = this.maxLength;
11784 if (this.disabled) {
11785 input.disabled=true;
11788 if (this.readOnly) {
11789 input.readonly=true;
11793 input.name = this.name;
11797 input.cls += ' input-' + this.size;
11801 ['xs','sm','md','lg'].map(function(size){
11802 if (settings[size]) {
11803 cfg.cls += ' col-' + size + '-' + settings[size];
11807 var inputblock = input;
11811 cls: 'glyphicon form-control-feedback'
11814 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11817 cls : 'has-feedback',
11825 if (this.before || this.after) {
11828 cls : 'input-group',
11832 if (this.before && typeof(this.before) == 'string') {
11834 inputblock.cn.push({
11836 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11840 if (this.before && typeof(this.before) == 'object') {
11841 this.before = Roo.factory(this.before);
11843 inputblock.cn.push({
11845 cls : 'roo-input-before input-group-prepend input-group-' +
11846 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11850 inputblock.cn.push(input);
11852 if (this.after && typeof(this.after) == 'string') {
11853 inputblock.cn.push({
11855 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11859 if (this.after && typeof(this.after) == 'object') {
11860 this.after = Roo.factory(this.after);
11862 inputblock.cn.push({
11864 cls : 'roo-input-after input-group-append input-group-' +
11865 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11869 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11870 inputblock.cls += ' has-feedback';
11871 inputblock.cn.push(feedback);
11876 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11877 tooltip : 'This field is required'
11879 if (this.allowBlank ) {
11880 indicator.style = this.allowBlank ? ' display:none' : '';
11882 if (align ==='left' && this.fieldLabel.length) {
11884 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11891 cls : 'control-label col-form-label',
11892 html : this.fieldLabel
11903 var labelCfg = cfg.cn[1];
11904 var contentCfg = cfg.cn[2];
11906 if(this.indicatorpos == 'right'){
11911 cls : 'control-label col-form-label',
11915 html : this.fieldLabel
11929 labelCfg = cfg.cn[0];
11930 contentCfg = cfg.cn[1];
11934 if(this.labelWidth > 12){
11935 labelCfg.style = "width: " + this.labelWidth + 'px';
11938 if(this.labelWidth < 13 && this.labelmd == 0){
11939 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11942 if(this.labellg > 0){
11943 labelCfg.cls += ' col-lg-' + this.labellg;
11944 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11947 if(this.labelmd > 0){
11948 labelCfg.cls += ' col-md-' + this.labelmd;
11949 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11952 if(this.labelsm > 0){
11953 labelCfg.cls += ' col-sm-' + this.labelsm;
11954 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11957 if(this.labelxs > 0){
11958 labelCfg.cls += ' col-xs-' + this.labelxs;
11959 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11963 } else if ( this.fieldLabel.length) {
11970 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11971 tooltip : 'This field is required',
11972 style : this.allowBlank ? ' display:none' : ''
11976 //cls : 'input-group-addon',
11977 html : this.fieldLabel
11985 if(this.indicatorpos == 'right'){
11990 //cls : 'input-group-addon',
11991 html : this.fieldLabel
11996 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11997 tooltip : 'This field is required',
11998 style : this.allowBlank ? ' display:none' : ''
12018 if (this.parentType === 'Navbar' && this.parent().bar) {
12019 cfg.cls += ' navbar-form';
12022 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12023 // on BS4 we do this only if not form
12024 cfg.cls += ' navbar-form';
12032 * return the real input element.
12034 inputEl: function ()
12036 return this.el.select('input.form-control',true).first();
12039 tooltipEl : function()
12041 return this.inputEl();
12044 indicatorEl : function()
12046 if (Roo.bootstrap.version == 4) {
12047 return false; // not enabled in v4 yet.
12050 var indicator = this.el.select('i.roo-required-indicator',true).first();
12060 setDisabled : function(v)
12062 var i = this.inputEl().dom;
12064 i.removeAttribute('disabled');
12068 i.setAttribute('disabled','true');
12070 initEvents : function()
12073 this.inputEl().on("keydown" , this.fireKey, this);
12074 this.inputEl().on("focus", this.onFocus, this);
12075 this.inputEl().on("blur", this.onBlur, this);
12077 this.inputEl().relayEvent('keyup', this);
12078 this.inputEl().relayEvent('paste', this);
12080 this.indicator = this.indicatorEl();
12082 if(this.indicator){
12083 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12086 // reference to original value for reset
12087 this.originalValue = this.getValue();
12088 //Roo.form.TextField.superclass.initEvents.call(this);
12089 if(this.validationEvent == 'keyup'){
12090 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12091 this.inputEl().on('keyup', this.filterValidation, this);
12093 else if(this.validationEvent !== false){
12094 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12097 if(this.selectOnFocus){
12098 this.on("focus", this.preFocus, this);
12101 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12102 this.inputEl().on("keypress", this.filterKeys, this);
12104 this.inputEl().relayEvent('keypress', this);
12107 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12108 this.el.on("click", this.autoSize, this);
12111 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12112 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12115 if (typeof(this.before) == 'object') {
12116 this.before.render(this.el.select('.roo-input-before',true).first());
12118 if (typeof(this.after) == 'object') {
12119 this.after.render(this.el.select('.roo-input-after',true).first());
12122 this.inputEl().on('change', this.onChange, this);
12125 filterValidation : function(e){
12126 if(!e.isNavKeyPress()){
12127 this.validationTask.delay(this.validationDelay);
12131 * Validates the field value
12132 * @return {Boolean} True if the value is valid, else false
12134 validate : function(){
12135 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12136 if(this.disabled || this.validateValue(this.getRawValue())){
12141 this.markInvalid();
12147 * Validates a value according to the field's validation rules and marks the field as invalid
12148 * if the validation fails
12149 * @param {Mixed} value The value to validate
12150 * @return {Boolean} True if the value is valid, else false
12152 validateValue : function(value)
12154 if(this.getVisibilityEl().hasClass('hidden')){
12158 if(value.length < 1) { // if it's blank
12159 if(this.allowBlank){
12165 if(value.length < this.minLength){
12168 if(value.length > this.maxLength){
12172 var vt = Roo.form.VTypes;
12173 if(!vt[this.vtype](value, this)){
12177 if(typeof this.validator == "function"){
12178 var msg = this.validator(value);
12182 if (typeof(msg) == 'string') {
12183 this.invalidText = msg;
12187 if(this.regex && !this.regex.test(value)){
12195 fireKey : function(e){
12196 //Roo.log('field ' + e.getKey());
12197 if(e.isNavKeyPress()){
12198 this.fireEvent("specialkey", this, e);
12201 focus : function (selectText){
12203 this.inputEl().focus();
12204 if(selectText === true){
12205 this.inputEl().dom.select();
12211 onFocus : function(){
12212 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12213 // this.el.addClass(this.focusClass);
12215 if(!this.hasFocus){
12216 this.hasFocus = true;
12217 this.startValue = this.getValue();
12218 this.fireEvent("focus", this);
12222 beforeBlur : Roo.emptyFn,
12226 onBlur : function(){
12228 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12229 //this.el.removeClass(this.focusClass);
12231 this.hasFocus = false;
12232 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12235 var v = this.getValue();
12236 if(String(v) !== String(this.startValue)){
12237 this.fireEvent('change', this, v, this.startValue);
12239 this.fireEvent("blur", this);
12242 onChange : function(e)
12244 var v = this.getValue();
12245 if(String(v) !== String(this.startValue)){
12246 this.fireEvent('change', this, v, this.startValue);
12252 * Resets the current field value to the originally loaded value and clears any validation messages
12254 reset : function(){
12255 this.setValue(this.originalValue);
12259 * Returns the name of the field
12260 * @return {Mixed} name The name field
12262 getName: function(){
12266 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12267 * @return {Mixed} value The field value
12269 getValue : function(){
12271 var v = this.inputEl().getValue();
12276 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12277 * @return {Mixed} value The field value
12279 getRawValue : function(){
12280 var v = this.inputEl().getValue();
12286 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12287 * @param {Mixed} value The value to set
12289 setRawValue : function(v){
12290 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12293 selectText : function(start, end){
12294 var v = this.getRawValue();
12296 start = start === undefined ? 0 : start;
12297 end = end === undefined ? v.length : end;
12298 var d = this.inputEl().dom;
12299 if(d.setSelectionRange){
12300 d.setSelectionRange(start, end);
12301 }else if(d.createTextRange){
12302 var range = d.createTextRange();
12303 range.moveStart("character", start);
12304 range.moveEnd("character", v.length-end);
12311 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12312 * @param {Mixed} value The value to set
12314 setValue : function(v){
12317 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12323 processValue : function(value){
12324 if(this.stripCharsRe){
12325 var newValue = value.replace(this.stripCharsRe, '');
12326 if(newValue !== value){
12327 this.setRawValue(newValue);
12334 preFocus : function(){
12336 if(this.selectOnFocus){
12337 this.inputEl().dom.select();
12340 filterKeys : function(e){
12341 var k = e.getKey();
12342 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12345 var c = e.getCharCode(), cc = String.fromCharCode(c);
12346 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12349 if(!this.maskRe.test(cc)){
12354 * Clear any invalid styles/messages for this field
12356 clearInvalid : function(){
12358 if(!this.el || this.preventMark){ // not rendered
12363 this.el.removeClass([this.invalidClass, 'is-invalid']);
12365 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12367 var feedback = this.el.select('.form-control-feedback', true).first();
12370 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12375 if(this.indicator){
12376 this.indicator.removeClass('visible');
12377 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12380 this.fireEvent('valid', this);
12384 * Mark this field as valid
12386 markValid : function()
12388 if(!this.el || this.preventMark){ // not rendered...
12392 this.el.removeClass([this.invalidClass, this.validClass]);
12393 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12395 var feedback = this.el.select('.form-control-feedback', true).first();
12398 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12401 if(this.indicator){
12402 this.indicator.removeClass('visible');
12403 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12411 if(this.allowBlank && !this.getRawValue().length){
12414 if (Roo.bootstrap.version == 3) {
12415 this.el.addClass(this.validClass);
12417 this.inputEl().addClass('is-valid');
12420 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12422 var feedback = this.el.select('.form-control-feedback', true).first();
12425 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12426 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12431 this.fireEvent('valid', this);
12435 * Mark this field as invalid
12436 * @param {String} msg The validation message
12438 markInvalid : function(msg)
12440 if(!this.el || this.preventMark){ // not rendered
12444 this.el.removeClass([this.invalidClass, this.validClass]);
12445 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12447 var feedback = this.el.select('.form-control-feedback', true).first();
12450 this.el.select('.form-control-feedback', true).first().removeClass(
12451 [this.invalidFeedbackClass, this.validFeedbackClass]);
12458 if(this.allowBlank && !this.getRawValue().length){
12462 if(this.indicator){
12463 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12464 this.indicator.addClass('visible');
12466 if (Roo.bootstrap.version == 3) {
12467 this.el.addClass(this.invalidClass);
12469 this.inputEl().addClass('is-invalid');
12474 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12476 var feedback = this.el.select('.form-control-feedback', true).first();
12479 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12481 if(this.getValue().length || this.forceFeedback){
12482 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12489 this.fireEvent('invalid', this, msg);
12492 SafariOnKeyDown : function(event)
12494 // this is a workaround for a password hang bug on chrome/ webkit.
12495 if (this.inputEl().dom.type != 'password') {
12499 var isSelectAll = false;
12501 if(this.inputEl().dom.selectionEnd > 0){
12502 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12504 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12505 event.preventDefault();
12510 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12512 event.preventDefault();
12513 // this is very hacky as keydown always get's upper case.
12515 var cc = String.fromCharCode(event.getCharCode());
12516 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12520 adjustWidth : function(tag, w){
12521 tag = tag.toLowerCase();
12522 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12523 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12524 if(tag == 'input'){
12527 if(tag == 'textarea'){
12530 }else if(Roo.isOpera){
12531 if(tag == 'input'){
12534 if(tag == 'textarea'){
12542 setFieldLabel : function(v)
12544 if(!this.rendered){
12548 if(this.indicatorEl()){
12549 var ar = this.el.select('label > span',true);
12551 if (ar.elements.length) {
12552 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12553 this.fieldLabel = v;
12557 var br = this.el.select('label',true);
12559 if(br.elements.length) {
12560 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12561 this.fieldLabel = v;
12565 Roo.log('Cannot Found any of label > span || label in input');
12569 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12570 this.fieldLabel = v;
12585 * @class Roo.bootstrap.TextArea
12586 * @extends Roo.bootstrap.Input
12587 * Bootstrap TextArea class
12588 * @cfg {Number} cols Specifies the visible width of a text area
12589 * @cfg {Number} rows Specifies the visible number of lines in a text area
12590 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12591 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12592 * @cfg {string} html text
12595 * Create a new TextArea
12596 * @param {Object} config The config object
12599 Roo.bootstrap.TextArea = function(config){
12600 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12604 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12614 getAutoCreate : function(){
12616 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12622 if(this.inputType != 'hidden'){
12623 cfg.cls = 'form-group' //input-group
12631 value : this.value || '',
12632 html: this.html || '',
12633 cls : 'form-control',
12634 placeholder : this.placeholder || ''
12638 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12639 input.maxLength = this.maxLength;
12643 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12647 input.cols = this.cols;
12650 if (this.readOnly) {
12651 input.readonly = true;
12655 input.name = this.name;
12659 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12663 ['xs','sm','md','lg'].map(function(size){
12664 if (settings[size]) {
12665 cfg.cls += ' col-' + size + '-' + settings[size];
12669 var inputblock = input;
12671 if(this.hasFeedback && !this.allowBlank){
12675 cls: 'glyphicon form-control-feedback'
12679 cls : 'has-feedback',
12688 if (this.before || this.after) {
12691 cls : 'input-group',
12695 inputblock.cn.push({
12697 cls : 'input-group-addon',
12702 inputblock.cn.push(input);
12704 if(this.hasFeedback && !this.allowBlank){
12705 inputblock.cls += ' has-feedback';
12706 inputblock.cn.push(feedback);
12710 inputblock.cn.push({
12712 cls : 'input-group-addon',
12719 if (align ==='left' && this.fieldLabel.length) {
12724 cls : 'control-label',
12725 html : this.fieldLabel
12736 if(this.labelWidth > 12){
12737 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12740 if(this.labelWidth < 13 && this.labelmd == 0){
12741 this.labelmd = this.labelWidth;
12744 if(this.labellg > 0){
12745 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12746 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12749 if(this.labelmd > 0){
12750 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12751 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12754 if(this.labelsm > 0){
12755 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12756 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12759 if(this.labelxs > 0){
12760 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12761 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12764 } else if ( this.fieldLabel.length) {
12769 //cls : 'input-group-addon',
12770 html : this.fieldLabel
12788 if (this.disabled) {
12789 input.disabled=true;
12796 * return the real textarea element.
12798 inputEl: function ()
12800 return this.el.select('textarea.form-control',true).first();
12804 * Clear any invalid styles/messages for this field
12806 clearInvalid : function()
12809 if(!this.el || this.preventMark){ // not rendered
12813 var label = this.el.select('label', true).first();
12814 var icon = this.el.select('i.fa-star', true).first();
12819 this.el.removeClass( this.validClass);
12820 this.inputEl().removeClass('is-invalid');
12822 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12824 var feedback = this.el.select('.form-control-feedback', true).first();
12827 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12832 this.fireEvent('valid', this);
12836 * Mark this field as valid
12838 markValid : function()
12840 if(!this.el || this.preventMark){ // not rendered
12844 this.el.removeClass([this.invalidClass, this.validClass]);
12845 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12847 var feedback = this.el.select('.form-control-feedback', true).first();
12850 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12853 if(this.disabled || this.allowBlank){
12857 var label = this.el.select('label', true).first();
12858 var icon = this.el.select('i.fa-star', true).first();
12863 if (Roo.bootstrap.version == 3) {
12864 this.el.addClass(this.validClass);
12866 this.inputEl().addClass('is-valid');
12870 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12872 var feedback = this.el.select('.form-control-feedback', true).first();
12875 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12876 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12881 this.fireEvent('valid', this);
12885 * Mark this field as invalid
12886 * @param {String} msg The validation message
12888 markInvalid : function(msg)
12890 if(!this.el || this.preventMark){ // not rendered
12894 this.el.removeClass([this.invalidClass, this.validClass]);
12895 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12897 var feedback = this.el.select('.form-control-feedback', true).first();
12900 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12903 if(this.disabled || this.allowBlank){
12907 var label = this.el.select('label', true).first();
12908 var icon = this.el.select('i.fa-star', true).first();
12910 if(!this.getValue().length && label && !icon){
12911 this.el.createChild({
12913 cls : 'text-danger fa fa-lg fa-star',
12914 tooltip : 'This field is required',
12915 style : 'margin-right:5px;'
12919 if (Roo.bootstrap.version == 3) {
12920 this.el.addClass(this.invalidClass);
12922 this.inputEl().addClass('is-invalid');
12925 // fixme ... this may be depricated need to test..
12926 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12928 var feedback = this.el.select('.form-control-feedback', true).first();
12931 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12933 if(this.getValue().length || this.forceFeedback){
12934 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12941 this.fireEvent('invalid', this, msg);
12949 * trigger field - base class for combo..
12954 * @class Roo.bootstrap.TriggerField
12955 * @extends Roo.bootstrap.Input
12956 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12957 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12958 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12959 * for which you can provide a custom implementation. For example:
12961 var trigger = new Roo.bootstrap.TriggerField();
12962 trigger.onTriggerClick = myTriggerFn;
12963 trigger.applyTo('my-field');
12966 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12967 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12968 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12969 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12970 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12973 * Create a new TriggerField.
12974 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12975 * to the base TextField)
12977 Roo.bootstrap.TriggerField = function(config){
12978 this.mimicing = false;
12979 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12982 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12984 * @cfg {String} triggerClass A CSS class to apply to the trigger
12987 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12992 * @cfg {Boolean} removable (true|false) special filter default false
12996 /** @cfg {Boolean} grow @hide */
12997 /** @cfg {Number} growMin @hide */
12998 /** @cfg {Number} growMax @hide */
13004 autoSize: Roo.emptyFn,
13008 deferHeight : true,
13011 actionMode : 'wrap',
13016 getAutoCreate : function(){
13018 var align = this.labelAlign || this.parentLabelAlign();
13023 cls: 'form-group' //input-group
13030 type : this.inputType,
13031 cls : 'form-control',
13032 autocomplete: 'new-password',
13033 placeholder : this.placeholder || ''
13037 input.name = this.name;
13040 input.cls += ' input-' + this.size;
13043 if (this.disabled) {
13044 input.disabled=true;
13047 var inputblock = input;
13049 if(this.hasFeedback && !this.allowBlank){
13053 cls: 'glyphicon form-control-feedback'
13056 if(this.removable && !this.editable ){
13058 cls : 'has-feedback',
13064 cls : 'roo-combo-removable-btn close'
13071 cls : 'has-feedback',
13080 if(this.removable && !this.editable ){
13082 cls : 'roo-removable',
13088 cls : 'roo-combo-removable-btn close'
13095 if (this.before || this.after) {
13098 cls : 'input-group',
13102 inputblock.cn.push({
13104 cls : 'input-group-addon input-group-prepend input-group-text',
13109 inputblock.cn.push(input);
13111 if(this.hasFeedback && !this.allowBlank){
13112 inputblock.cls += ' has-feedback';
13113 inputblock.cn.push(feedback);
13117 inputblock.cn.push({
13119 cls : 'input-group-addon input-group-append input-group-text',
13128 var ibwrap = inputblock;
13133 cls: 'roo-select2-choices',
13137 cls: 'roo-select2-search-field',
13149 cls: 'roo-select2-container input-group',
13154 cls: 'form-hidden-field'
13160 if(!this.multiple && this.showToggleBtn){
13166 if (this.caret != false) {
13169 cls: 'fa fa-' + this.caret
13176 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13178 Roo.bootstrap.version == 3 ? caret : '',
13181 cls: 'combobox-clear',
13195 combobox.cls += ' roo-select2-container-multi';
13199 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13200 tooltip : 'This field is required'
13202 if (Roo.bootstrap.version == 4) {
13205 style : 'display:none'
13210 if (align ==='left' && this.fieldLabel.length) {
13212 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13219 cls : 'control-label',
13220 html : this.fieldLabel
13232 var labelCfg = cfg.cn[1];
13233 var contentCfg = cfg.cn[2];
13235 if(this.indicatorpos == 'right'){
13240 cls : 'control-label',
13244 html : this.fieldLabel
13258 labelCfg = cfg.cn[0];
13259 contentCfg = cfg.cn[1];
13262 if(this.labelWidth > 12){
13263 labelCfg.style = "width: " + this.labelWidth + 'px';
13266 if(this.labelWidth < 13 && this.labelmd == 0){
13267 this.labelmd = this.labelWidth;
13270 if(this.labellg > 0){
13271 labelCfg.cls += ' col-lg-' + this.labellg;
13272 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13275 if(this.labelmd > 0){
13276 labelCfg.cls += ' col-md-' + this.labelmd;
13277 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13280 if(this.labelsm > 0){
13281 labelCfg.cls += ' col-sm-' + this.labelsm;
13282 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13285 if(this.labelxs > 0){
13286 labelCfg.cls += ' col-xs-' + this.labelxs;
13287 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13290 } else if ( this.fieldLabel.length) {
13291 // Roo.log(" label");
13296 //cls : 'input-group-addon',
13297 html : this.fieldLabel
13305 if(this.indicatorpos == 'right'){
13313 html : this.fieldLabel
13327 // Roo.log(" no label && no align");
13334 ['xs','sm','md','lg'].map(function(size){
13335 if (settings[size]) {
13336 cfg.cls += ' col-' + size + '-' + settings[size];
13347 onResize : function(w, h){
13348 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13349 // if(typeof w == 'number'){
13350 // var x = w - this.trigger.getWidth();
13351 // this.inputEl().setWidth(this.adjustWidth('input', x));
13352 // this.trigger.setStyle('left', x+'px');
13357 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13360 getResizeEl : function(){
13361 return this.inputEl();
13365 getPositionEl : function(){
13366 return this.inputEl();
13370 alignErrorIcon : function(){
13371 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13375 initEvents : function(){
13379 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13380 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13381 if(!this.multiple && this.showToggleBtn){
13382 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13383 if(this.hideTrigger){
13384 this.trigger.setDisplayed(false);
13386 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13390 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13393 if(this.removable && !this.editable && !this.tickable){
13394 var close = this.closeTriggerEl();
13397 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13398 close.on('click', this.removeBtnClick, this, close);
13402 //this.trigger.addClassOnOver('x-form-trigger-over');
13403 //this.trigger.addClassOnClick('x-form-trigger-click');
13406 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13410 closeTriggerEl : function()
13412 var close = this.el.select('.roo-combo-removable-btn', true).first();
13413 return close ? close : false;
13416 removeBtnClick : function(e, h, el)
13418 e.preventDefault();
13420 if(this.fireEvent("remove", this) !== false){
13422 this.fireEvent("afterremove", this)
13426 createList : function()
13428 this.list = Roo.get(document.body).createChild({
13429 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13430 cls: 'typeahead typeahead-long dropdown-menu shadow',
13431 style: 'display:none'
13434 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13439 initTrigger : function(){
13444 onDestroy : function(){
13446 this.trigger.removeAllListeners();
13447 // this.trigger.remove();
13450 // this.wrap.remove();
13452 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13456 onFocus : function(){
13457 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13459 if(!this.mimicing){
13460 this.wrap.addClass('x-trigger-wrap-focus');
13461 this.mimicing = true;
13462 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13463 if(this.monitorTab){
13464 this.el.on("keydown", this.checkTab, this);
13471 checkTab : function(e){
13472 if(e.getKey() == e.TAB){
13473 this.triggerBlur();
13478 onBlur : function(){
13483 mimicBlur : function(e, t){
13485 if(!this.wrap.contains(t) && this.validateBlur()){
13486 this.triggerBlur();
13492 triggerBlur : function(){
13493 this.mimicing = false;
13494 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13495 if(this.monitorTab){
13496 this.el.un("keydown", this.checkTab, this);
13498 //this.wrap.removeClass('x-trigger-wrap-focus');
13499 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13503 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13504 validateBlur : function(e, t){
13509 onDisable : function(){
13510 this.inputEl().dom.disabled = true;
13511 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13513 // this.wrap.addClass('x-item-disabled');
13518 onEnable : function(){
13519 this.inputEl().dom.disabled = false;
13520 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13522 // this.el.removeClass('x-item-disabled');
13527 onShow : function(){
13528 var ae = this.getActionEl();
13531 ae.dom.style.display = '';
13532 ae.dom.style.visibility = 'visible';
13538 onHide : function(){
13539 var ae = this.getActionEl();
13540 ae.dom.style.display = 'none';
13544 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13545 * by an implementing function.
13547 * @param {EventObject} e
13549 onTriggerClick : Roo.emptyFn
13557 * @class Roo.bootstrap.CardUploader
13558 * @extends Roo.bootstrap.Button
13559 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13560 * @cfg {Number} errorTimeout default 3000
13561 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13562 * @cfg {Array} html The button text.
13566 * Create a new CardUploader
13567 * @param {Object} config The config object
13570 Roo.bootstrap.CardUploader = function(config){
13574 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13577 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13585 * When a image is clicked on - and needs to display a slideshow or similar..
13586 * @param {Roo.bootstrap.Card} this
13587 * @param {Object} The image information data
13593 * When a the download link is clicked
13594 * @param {Roo.bootstrap.Card} this
13595 * @param {Object} The image information data contains
13602 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13605 errorTimeout : 3000,
13609 fileCollection : false,
13612 getAutoCreate : function()
13616 cls :'form-group' ,
13621 //cls : 'input-group-addon',
13622 html : this.fieldLabel
13630 value : this.value,
13631 cls : 'd-none form-control'
13636 multiple : 'multiple',
13638 cls : 'd-none roo-card-upload-selector'
13642 cls : 'roo-card-uploader-button-container w-100 mb-2'
13645 cls : 'card-columns roo-card-uploader-container'
13655 getChildContainer : function() /// what children are added to.
13657 return this.containerEl;
13660 getButtonContainer : function() /// what children are added to.
13662 return this.el.select(".roo-card-uploader-button-container").first();
13665 initEvents : function()
13668 Roo.bootstrap.Input.prototype.initEvents.call(this);
13672 xns: Roo.bootstrap,
13675 container_method : 'getButtonContainer' ,
13676 html : this.html, // fix changable?
13679 'click' : function(btn, e) {
13688 this.urlAPI = (window.createObjectURL && window) ||
13689 (window.URL && URL.revokeObjectURL && URL) ||
13690 (window.webkitURL && webkitURL);
13695 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13697 this.selectorEl.on('change', this.onFileSelected, this);
13700 this.images.forEach(function(img) {
13703 this.images = false;
13705 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13711 onClick : function(e)
13713 e.preventDefault();
13715 this.selectorEl.dom.click();
13719 onFileSelected : function(e)
13721 e.preventDefault();
13723 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13727 Roo.each(this.selectorEl.dom.files, function(file){
13728 this.addFile(file);
13737 addFile : function(file)
13740 if(typeof(file) === 'string'){
13741 throw "Add file by name?"; // should not happen
13745 if(!file || !this.urlAPI){
13755 var url = _this.urlAPI.createObjectURL( file);
13758 id : Roo.bootstrap.CardUploader.ID--,
13759 is_uploaded : false,
13763 mimetype : file.type,
13771 * addCard - add an Attachment to the uploader
13772 * @param data - the data about the image to upload
13776 title : "Title of file",
13777 is_uploaded : false,
13778 src : "http://.....",
13779 srcfile : { the File upload object },
13780 mimetype : file.type,
13783 .. any other data...
13789 addCard : function (data)
13791 // hidden input element?
13792 // if the file is not an image...
13793 //then we need to use something other that and header_image
13798 xns : Roo.bootstrap,
13799 xtype : 'CardFooter',
13802 xns : Roo.bootstrap,
13808 xns : Roo.bootstrap,
13810 html : String.format("<small>{0}</small>", data.title),
13811 cls : 'col-10 text-left',
13816 click : function() {
13818 t.fireEvent( "download", t, data );
13824 xns : Roo.bootstrap,
13826 style: 'max-height: 28px; ',
13832 click : function() {
13833 t.removeCard(data.id)
13845 var cn = this.addxtype(
13848 xns : Roo.bootstrap,
13851 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13852 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13853 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13858 initEvents : function() {
13859 Roo.bootstrap.Card.prototype.initEvents.call(this);
13861 this.imgEl = this.el.select('.card-img-top').first();
13863 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13864 this.imgEl.set({ 'pointer' : 'cursor' });
13867 this.getCardFooter().addClass('p-1');
13874 // dont' really need ot update items.
13875 // this.items.push(cn);
13876 this.fileCollection.add(cn);
13878 if (!data.srcfile) {
13879 this.updateInput();
13884 var reader = new FileReader();
13885 reader.addEventListener("load", function() {
13886 data.srcdata = reader.result;
13889 reader.readAsDataURL(data.srcfile);
13894 removeCard : function(id)
13897 var card = this.fileCollection.get(id);
13898 card.data.is_deleted = 1;
13899 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13900 //this.fileCollection.remove(card);
13901 //this.items = this.items.filter(function(e) { return e != card });
13902 // dont' really need ot update items.
13903 card.el.dom.parentNode.removeChild(card.el.dom);
13904 this.updateInput();
13910 this.fileCollection.each(function(card) {
13911 if (card.el.dom && card.el.dom.parentNode) {
13912 card.el.dom.parentNode.removeChild(card.el.dom);
13915 this.fileCollection.clear();
13916 this.updateInput();
13919 updateInput : function()
13922 this.fileCollection.each(function(e) {
13926 this.inputEl().dom.value = JSON.stringify(data);
13936 Roo.bootstrap.CardUploader.ID = -1;/*
13938 * Ext JS Library 1.1.1
13939 * Copyright(c) 2006-2007, Ext JS, LLC.
13941 * Originally Released Under LGPL - original licence link has changed is not relivant.
13944 * <script type="text/javascript">
13949 * @class Roo.data.SortTypes
13951 * Defines the default sorting (casting?) comparison functions used when sorting data.
13953 Roo.data.SortTypes = {
13955 * Default sort that does nothing
13956 * @param {Mixed} s The value being converted
13957 * @return {Mixed} The comparison value
13959 none : function(s){
13964 * The regular expression used to strip tags
13968 stripTagsRE : /<\/?[^>]+>/gi,
13971 * Strips all HTML tags to sort on text only
13972 * @param {Mixed} s The value being converted
13973 * @return {String} The comparison value
13975 asText : function(s){
13976 return String(s).replace(this.stripTagsRE, "");
13980 * Strips all HTML tags to sort on text only - Case insensitive
13981 * @param {Mixed} s The value being converted
13982 * @return {String} The comparison value
13984 asUCText : function(s){
13985 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13989 * Case insensitive string
13990 * @param {Mixed} s The value being converted
13991 * @return {String} The comparison value
13993 asUCString : function(s) {
13994 return String(s).toUpperCase();
13999 * @param {Mixed} s The value being converted
14000 * @return {Number} The comparison value
14002 asDate : function(s) {
14006 if(s instanceof Date){
14007 return s.getTime();
14009 return Date.parse(String(s));
14014 * @param {Mixed} s The value being converted
14015 * @return {Float} The comparison value
14017 asFloat : function(s) {
14018 var val = parseFloat(String(s).replace(/,/g, ""));
14027 * @param {Mixed} s The value being converted
14028 * @return {Number} The comparison value
14030 asInt : function(s) {
14031 var val = parseInt(String(s).replace(/,/g, ""));
14039 * Ext JS Library 1.1.1
14040 * Copyright(c) 2006-2007, Ext JS, LLC.
14042 * Originally Released Under LGPL - original licence link has changed is not relivant.
14045 * <script type="text/javascript">
14049 * @class Roo.data.Record
14050 * Instances of this class encapsulate both record <em>definition</em> information, and record
14051 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14052 * to access Records cached in an {@link Roo.data.Store} object.<br>
14054 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14055 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14058 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14060 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14061 * {@link #create}. The parameters are the same.
14062 * @param {Array} data An associative Array of data values keyed by the field name.
14063 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14064 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14065 * not specified an integer id is generated.
14067 Roo.data.Record = function(data, id){
14068 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14073 * Generate a constructor for a specific record layout.
14074 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14075 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14076 * Each field definition object may contain the following properties: <ul>
14077 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14078 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14079 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14080 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14081 * is being used, then this is a string containing the javascript expression to reference the data relative to
14082 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14083 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14084 * this may be omitted.</p></li>
14085 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14086 * <ul><li>auto (Default, implies no conversion)</li>
14091 * <li>date</li></ul></p></li>
14092 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14093 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14094 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14095 * by the Reader into an object that will be stored in the Record. It is passed the
14096 * following parameters:<ul>
14097 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14099 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14101 * <br>usage:<br><pre><code>
14102 var TopicRecord = Roo.data.Record.create(
14103 {name: 'title', mapping: 'topic_title'},
14104 {name: 'author', mapping: 'username'},
14105 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14106 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14107 {name: 'lastPoster', mapping: 'user2'},
14108 {name: 'excerpt', mapping: 'post_text'}
14111 var myNewRecord = new TopicRecord({
14112 title: 'Do my job please',
14115 lastPost: new Date(),
14116 lastPoster: 'Animal',
14117 excerpt: 'No way dude!'
14119 myStore.add(myNewRecord);
14124 Roo.data.Record.create = function(o){
14125 var f = function(){
14126 f.superclass.constructor.apply(this, arguments);
14128 Roo.extend(f, Roo.data.Record);
14129 var p = f.prototype;
14130 p.fields = new Roo.util.MixedCollection(false, function(field){
14133 for(var i = 0, len = o.length; i < len; i++){
14134 p.fields.add(new Roo.data.Field(o[i]));
14136 f.getField = function(name){
14137 return p.fields.get(name);
14142 Roo.data.Record.AUTO_ID = 1000;
14143 Roo.data.Record.EDIT = 'edit';
14144 Roo.data.Record.REJECT = 'reject';
14145 Roo.data.Record.COMMIT = 'commit';
14147 Roo.data.Record.prototype = {
14149 * Readonly flag - true if this record has been modified.
14158 join : function(store){
14159 this.store = store;
14163 * Set the named field to the specified value.
14164 * @param {String} name The name of the field to set.
14165 * @param {Object} value The value to set the field to.
14167 set : function(name, value){
14168 if(this.data[name] == value){
14172 if(!this.modified){
14173 this.modified = {};
14175 if(typeof this.modified[name] == 'undefined'){
14176 this.modified[name] = this.data[name];
14178 this.data[name] = value;
14179 if(!this.editing && this.store){
14180 this.store.afterEdit(this);
14185 * Get the value of the named field.
14186 * @param {String} name The name of the field to get the value of.
14187 * @return {Object} The value of the field.
14189 get : function(name){
14190 return this.data[name];
14194 beginEdit : function(){
14195 this.editing = true;
14196 this.modified = {};
14200 cancelEdit : function(){
14201 this.editing = false;
14202 delete this.modified;
14206 endEdit : function(){
14207 this.editing = false;
14208 if(this.dirty && this.store){
14209 this.store.afterEdit(this);
14214 * Usually called by the {@link Roo.data.Store} which owns the Record.
14215 * Rejects all changes made to the Record since either creation, or the last commit operation.
14216 * Modified fields are reverted to their original values.
14218 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14219 * of reject operations.
14221 reject : function(){
14222 var m = this.modified;
14224 if(typeof m[n] != "function"){
14225 this.data[n] = m[n];
14228 this.dirty = false;
14229 delete this.modified;
14230 this.editing = false;
14232 this.store.afterReject(this);
14237 * Usually called by the {@link Roo.data.Store} which owns the Record.
14238 * Commits all changes made to the Record since either creation, or the last commit operation.
14240 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14241 * of commit operations.
14243 commit : function(){
14244 this.dirty = false;
14245 delete this.modified;
14246 this.editing = false;
14248 this.store.afterCommit(this);
14253 hasError : function(){
14254 return this.error != null;
14258 clearError : function(){
14263 * Creates a copy of this record.
14264 * @param {String} id (optional) A new record id if you don't want to use this record's id
14267 copy : function(newId) {
14268 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14272 * Ext JS Library 1.1.1
14273 * Copyright(c) 2006-2007, Ext JS, LLC.
14275 * Originally Released Under LGPL - original licence link has changed is not relivant.
14278 * <script type="text/javascript">
14284 * @class Roo.data.Store
14285 * @extends Roo.util.Observable
14286 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14287 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14289 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14290 * has no knowledge of the format of the data returned by the Proxy.<br>
14292 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14293 * instances from the data object. These records are cached and made available through accessor functions.
14295 * Creates a new Store.
14296 * @param {Object} config A config object containing the objects needed for the Store to access data,
14297 * and read the data into Records.
14299 Roo.data.Store = function(config){
14300 this.data = new Roo.util.MixedCollection(false);
14301 this.data.getKey = function(o){
14304 this.baseParams = {};
14306 this.paramNames = {
14311 "multisort" : "_multisort"
14314 if(config && config.data){
14315 this.inlineData = config.data;
14316 delete config.data;
14319 Roo.apply(this, config);
14321 if(this.reader){ // reader passed
14322 this.reader = Roo.factory(this.reader, Roo.data);
14323 this.reader.xmodule = this.xmodule || false;
14324 if(!this.recordType){
14325 this.recordType = this.reader.recordType;
14327 if(this.reader.onMetaChange){
14328 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14332 if(this.recordType){
14333 this.fields = this.recordType.prototype.fields;
14335 this.modified = [];
14339 * @event datachanged
14340 * Fires when the data cache has changed, and a widget which is using this Store
14341 * as a Record cache should refresh its view.
14342 * @param {Store} this
14344 datachanged : true,
14346 * @event metachange
14347 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14348 * @param {Store} this
14349 * @param {Object} meta The JSON metadata
14354 * Fires when Records have been added to the Store
14355 * @param {Store} this
14356 * @param {Roo.data.Record[]} records The array of Records added
14357 * @param {Number} index The index at which the record(s) were added
14362 * Fires when a Record has been removed from the Store
14363 * @param {Store} this
14364 * @param {Roo.data.Record} record The Record that was removed
14365 * @param {Number} index The index at which the record was removed
14370 * Fires when a Record has been updated
14371 * @param {Store} this
14372 * @param {Roo.data.Record} record The Record that was updated
14373 * @param {String} operation The update operation being performed. Value may be one of:
14375 Roo.data.Record.EDIT
14376 Roo.data.Record.REJECT
14377 Roo.data.Record.COMMIT
14383 * Fires when the data cache has been cleared.
14384 * @param {Store} this
14388 * @event beforeload
14389 * Fires before a request is made for a new data object. If the beforeload handler returns false
14390 * the load action will be canceled.
14391 * @param {Store} this
14392 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14396 * @event beforeloadadd
14397 * Fires after a new set of Records has been loaded.
14398 * @param {Store} this
14399 * @param {Roo.data.Record[]} records The Records that were loaded
14400 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14402 beforeloadadd : true,
14405 * Fires after a new set of Records has been loaded, before they are added to the store.
14406 * @param {Store} this
14407 * @param {Roo.data.Record[]} records The Records that were loaded
14408 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14409 * @params {Object} return from reader
14413 * @event loadexception
14414 * Fires if an exception occurs in the Proxy during loading.
14415 * Called with the signature of the Proxy's "loadexception" event.
14416 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14419 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14420 * @param {Object} load options
14421 * @param {Object} jsonData from your request (normally this contains the Exception)
14423 loadexception : true
14427 this.proxy = Roo.factory(this.proxy, Roo.data);
14428 this.proxy.xmodule = this.xmodule || false;
14429 this.relayEvents(this.proxy, ["loadexception"]);
14431 this.sortToggle = {};
14432 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14434 Roo.data.Store.superclass.constructor.call(this);
14436 if(this.inlineData){
14437 this.loadData(this.inlineData);
14438 delete this.inlineData;
14442 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14444 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14445 * without a remote query - used by combo/forms at present.
14449 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14452 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14455 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14456 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14459 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14460 * on any HTTP request
14463 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14466 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14470 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14471 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14473 remoteSort : false,
14476 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14477 * loaded or when a record is removed. (defaults to false).
14479 pruneModifiedRecords : false,
14482 lastOptions : null,
14485 * Add Records to the Store and fires the add event.
14486 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14488 add : function(records){
14489 records = [].concat(records);
14490 for(var i = 0, len = records.length; i < len; i++){
14491 records[i].join(this);
14493 var index = this.data.length;
14494 this.data.addAll(records);
14495 this.fireEvent("add", this, records, index);
14499 * Remove a Record from the Store and fires the remove event.
14500 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14502 remove : function(record){
14503 var index = this.data.indexOf(record);
14504 this.data.removeAt(index);
14506 if(this.pruneModifiedRecords){
14507 this.modified.remove(record);
14509 this.fireEvent("remove", this, record, index);
14513 * Remove all Records from the Store and fires the clear event.
14515 removeAll : function(){
14517 if(this.pruneModifiedRecords){
14518 this.modified = [];
14520 this.fireEvent("clear", this);
14524 * Inserts Records to the Store at the given index and fires the add event.
14525 * @param {Number} index The start index at which to insert the passed Records.
14526 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14528 insert : function(index, records){
14529 records = [].concat(records);
14530 for(var i = 0, len = records.length; i < len; i++){
14531 this.data.insert(index, records[i]);
14532 records[i].join(this);
14534 this.fireEvent("add", this, records, index);
14538 * Get the index within the cache of the passed Record.
14539 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14540 * @return {Number} The index of the passed Record. Returns -1 if not found.
14542 indexOf : function(record){
14543 return this.data.indexOf(record);
14547 * Get the index within the cache of the Record with the passed id.
14548 * @param {String} id The id of the Record to find.
14549 * @return {Number} The index of the Record. Returns -1 if not found.
14551 indexOfId : function(id){
14552 return this.data.indexOfKey(id);
14556 * Get the Record with the specified id.
14557 * @param {String} id The id of the Record to find.
14558 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14560 getById : function(id){
14561 return this.data.key(id);
14565 * Get the Record at the specified index.
14566 * @param {Number} index The index of the Record to find.
14567 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14569 getAt : function(index){
14570 return this.data.itemAt(index);
14574 * Returns a range of Records between specified indices.
14575 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14576 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14577 * @return {Roo.data.Record[]} An array of Records
14579 getRange : function(start, end){
14580 return this.data.getRange(start, end);
14584 storeOptions : function(o){
14585 o = Roo.apply({}, o);
14588 this.lastOptions = o;
14592 * Loads the Record cache from the configured Proxy using the configured Reader.
14594 * If using remote paging, then the first load call must specify the <em>start</em>
14595 * and <em>limit</em> properties in the options.params property to establish the initial
14596 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14598 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14599 * and this call will return before the new data has been loaded. Perform any post-processing
14600 * in a callback function, or in a "load" event handler.</strong>
14602 * @param {Object} options An object containing properties which control loading options:<ul>
14603 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14604 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14605 * passed the following arguments:<ul>
14606 * <li>r : Roo.data.Record[]</li>
14607 * <li>options: Options object from the load call</li>
14608 * <li>success: Boolean success indicator</li></ul></li>
14609 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14610 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14613 load : function(options){
14614 options = options || {};
14615 if(this.fireEvent("beforeload", this, options) !== false){
14616 this.storeOptions(options);
14617 var p = Roo.apply(options.params || {}, this.baseParams);
14618 // if meta was not loaded from remote source.. try requesting it.
14619 if (!this.reader.metaFromRemote) {
14620 p._requestMeta = 1;
14622 if(this.sortInfo && this.remoteSort){
14623 var pn = this.paramNames;
14624 p[pn["sort"]] = this.sortInfo.field;
14625 p[pn["dir"]] = this.sortInfo.direction;
14627 if (this.multiSort) {
14628 var pn = this.paramNames;
14629 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14632 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14637 * Reloads the Record cache from the configured Proxy using the configured Reader and
14638 * the options from the last load operation performed.
14639 * @param {Object} options (optional) An object containing properties which may override the options
14640 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14641 * the most recently used options are reused).
14643 reload : function(options){
14644 this.load(Roo.applyIf(options||{}, this.lastOptions));
14648 // Called as a callback by the Reader during a load operation.
14649 loadRecords : function(o, options, success){
14650 if(!o || success === false){
14651 if(success !== false){
14652 this.fireEvent("load", this, [], options, o);
14654 if(options.callback){
14655 options.callback.call(options.scope || this, [], options, false);
14659 // if data returned failure - throw an exception.
14660 if (o.success === false) {
14661 // show a message if no listener is registered.
14662 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14663 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14665 // loadmask wil be hooked into this..
14666 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14669 var r = o.records, t = o.totalRecords || r.length;
14671 this.fireEvent("beforeloadadd", this, r, options, o);
14673 if(!options || options.add !== true){
14674 if(this.pruneModifiedRecords){
14675 this.modified = [];
14677 for(var i = 0, len = r.length; i < len; i++){
14681 this.data = this.snapshot;
14682 delete this.snapshot;
14685 this.data.addAll(r);
14686 this.totalLength = t;
14688 this.fireEvent("datachanged", this);
14690 this.totalLength = Math.max(t, this.data.length+r.length);
14694 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14696 var e = new Roo.data.Record({});
14698 e.set(this.parent.displayField, this.parent.emptyTitle);
14699 e.set(this.parent.valueField, '');
14704 this.fireEvent("load", this, r, options, o);
14705 if(options.callback){
14706 options.callback.call(options.scope || this, r, options, true);
14712 * Loads data from a passed data block. A Reader which understands the format of the data
14713 * must have been configured in the constructor.
14714 * @param {Object} data The data block from which to read the Records. The format of the data expected
14715 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14716 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14718 loadData : function(o, append){
14719 var r = this.reader.readRecords(o);
14720 this.loadRecords(r, {add: append}, true);
14724 * using 'cn' the nested child reader read the child array into it's child stores.
14725 * @param {Object} rec The record with a 'children array
14727 loadDataFromChildren : function(rec)
14729 this.loadData(this.reader.toLoadData(rec));
14734 * Gets the number of cached records.
14736 * <em>If using paging, this may not be the total size of the dataset. If the data object
14737 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14738 * the data set size</em>
14740 getCount : function(){
14741 return this.data.length || 0;
14745 * Gets the total number of records in the dataset as returned by the server.
14747 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14748 * the dataset size</em>
14750 getTotalCount : function(){
14751 return this.totalLength || 0;
14755 * Returns the sort state of the Store as an object with two properties:
14757 field {String} The name of the field by which the Records are sorted
14758 direction {String} The sort order, "ASC" or "DESC"
14761 getSortState : function(){
14762 return this.sortInfo;
14766 applySort : function(){
14767 if(this.sortInfo && !this.remoteSort){
14768 var s = this.sortInfo, f = s.field;
14769 var st = this.fields.get(f).sortType;
14770 var fn = function(r1, r2){
14771 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14772 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14774 this.data.sort(s.direction, fn);
14775 if(this.snapshot && this.snapshot != this.data){
14776 this.snapshot.sort(s.direction, fn);
14782 * Sets the default sort column and order to be used by the next load operation.
14783 * @param {String} fieldName The name of the field to sort by.
14784 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14786 setDefaultSort : function(field, dir){
14787 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14791 * Sort the Records.
14792 * If remote sorting is used, the sort is performed on the server, and the cache is
14793 * reloaded. If local sorting is used, the cache is sorted internally.
14794 * @param {String} fieldName The name of the field to sort by.
14795 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14797 sort : function(fieldName, dir){
14798 var f = this.fields.get(fieldName);
14800 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14802 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14803 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14808 this.sortToggle[f.name] = dir;
14809 this.sortInfo = {field: f.name, direction: dir};
14810 if(!this.remoteSort){
14812 this.fireEvent("datachanged", this);
14814 this.load(this.lastOptions);
14819 * Calls the specified function for each of the Records in the cache.
14820 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14821 * Returning <em>false</em> aborts and exits the iteration.
14822 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14824 each : function(fn, scope){
14825 this.data.each(fn, scope);
14829 * Gets all records modified since the last commit. Modified records are persisted across load operations
14830 * (e.g., during paging).
14831 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14833 getModifiedRecords : function(){
14834 return this.modified;
14838 createFilterFn : function(property, value, anyMatch){
14839 if(!value.exec){ // not a regex
14840 value = String(value);
14841 if(value.length == 0){
14844 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14846 return function(r){
14847 return value.test(r.data[property]);
14852 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14853 * @param {String} property A field on your records
14854 * @param {Number} start The record index to start at (defaults to 0)
14855 * @param {Number} end The last record index to include (defaults to length - 1)
14856 * @return {Number} The sum
14858 sum : function(property, start, end){
14859 var rs = this.data.items, v = 0;
14860 start = start || 0;
14861 end = (end || end === 0) ? end : rs.length-1;
14863 for(var i = start; i <= end; i++){
14864 v += (rs[i].data[property] || 0);
14870 * Filter the records by a specified property.
14871 * @param {String} field A field on your records
14872 * @param {String/RegExp} value Either a string that the field
14873 * should start with or a RegExp to test against the field
14874 * @param {Boolean} anyMatch True to match any part not just the beginning
14876 filter : function(property, value, anyMatch){
14877 var fn = this.createFilterFn(property, value, anyMatch);
14878 return fn ? this.filterBy(fn) : this.clearFilter();
14882 * Filter by a function. The specified function will be called with each
14883 * record in this data source. If the function returns true the record is included,
14884 * otherwise it is filtered.
14885 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14886 * @param {Object} scope (optional) The scope of the function (defaults to this)
14888 filterBy : function(fn, scope){
14889 this.snapshot = this.snapshot || this.data;
14890 this.data = this.queryBy(fn, scope||this);
14891 this.fireEvent("datachanged", this);
14895 * Query the records by a specified property.
14896 * @param {String} field A field on your records
14897 * @param {String/RegExp} value Either a string that the field
14898 * should start with or a RegExp to test against the field
14899 * @param {Boolean} anyMatch True to match any part not just the beginning
14900 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14902 query : function(property, value, anyMatch){
14903 var fn = this.createFilterFn(property, value, anyMatch);
14904 return fn ? this.queryBy(fn) : this.data.clone();
14908 * Query by a function. The specified function will be called with each
14909 * record in this data source. If the function returns true the record is included
14911 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14912 * @param {Object} scope (optional) The scope of the function (defaults to this)
14913 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14915 queryBy : function(fn, scope){
14916 var data = this.snapshot || this.data;
14917 return data.filterBy(fn, scope||this);
14921 * Collects unique values for a particular dataIndex from this store.
14922 * @param {String} dataIndex The property to collect
14923 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14924 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14925 * @return {Array} An array of the unique values
14927 collect : function(dataIndex, allowNull, bypassFilter){
14928 var d = (bypassFilter === true && this.snapshot) ?
14929 this.snapshot.items : this.data.items;
14930 var v, sv, r = [], l = {};
14931 for(var i = 0, len = d.length; i < len; i++){
14932 v = d[i].data[dataIndex];
14934 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14943 * Revert to a view of the Record cache with no filtering applied.
14944 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14946 clearFilter : function(suppressEvent){
14947 if(this.snapshot && this.snapshot != this.data){
14948 this.data = this.snapshot;
14949 delete this.snapshot;
14950 if(suppressEvent !== true){
14951 this.fireEvent("datachanged", this);
14957 afterEdit : function(record){
14958 if(this.modified.indexOf(record) == -1){
14959 this.modified.push(record);
14961 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14965 afterReject : function(record){
14966 this.modified.remove(record);
14967 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14971 afterCommit : function(record){
14972 this.modified.remove(record);
14973 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14977 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14978 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14980 commitChanges : function(){
14981 var m = this.modified.slice(0);
14982 this.modified = [];
14983 for(var i = 0, len = m.length; i < len; i++){
14989 * Cancel outstanding changes on all changed records.
14991 rejectChanges : function(){
14992 var m = this.modified.slice(0);
14993 this.modified = [];
14994 for(var i = 0, len = m.length; i < len; i++){
14999 onMetaChange : function(meta, rtype, o){
15000 this.recordType = rtype;
15001 this.fields = rtype.prototype.fields;
15002 delete this.snapshot;
15003 this.sortInfo = meta.sortInfo || this.sortInfo;
15004 this.modified = [];
15005 this.fireEvent('metachange', this, this.reader.meta);
15008 moveIndex : function(data, type)
15010 var index = this.indexOf(data);
15012 var newIndex = index + type;
15016 this.insert(newIndex, data);
15021 * Ext JS Library 1.1.1
15022 * Copyright(c) 2006-2007, Ext JS, LLC.
15024 * Originally Released Under LGPL - original licence link has changed is not relivant.
15027 * <script type="text/javascript">
15031 * @class Roo.data.SimpleStore
15032 * @extends Roo.data.Store
15033 * Small helper class to make creating Stores from Array data easier.
15034 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15035 * @cfg {Array} fields An array of field definition objects, or field name strings.
15036 * @cfg {Object} an existing reader (eg. copied from another store)
15037 * @cfg {Array} data The multi-dimensional array of data
15039 * @param {Object} config
15041 Roo.data.SimpleStore = function(config)
15043 Roo.data.SimpleStore.superclass.constructor.call(this, {
15045 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15048 Roo.data.Record.create(config.fields)
15050 proxy : new Roo.data.MemoryProxy(config.data)
15054 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15056 * Ext JS Library 1.1.1
15057 * Copyright(c) 2006-2007, Ext JS, LLC.
15059 * Originally Released Under LGPL - original licence link has changed is not relivant.
15062 * <script type="text/javascript">
15067 * @extends Roo.data.Store
15068 * @class Roo.data.JsonStore
15069 * Small helper class to make creating Stores for JSON data easier. <br/>
15071 var store = new Roo.data.JsonStore({
15072 url: 'get-images.php',
15074 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15077 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15078 * JsonReader and HttpProxy (unless inline data is provided).</b>
15079 * @cfg {Array} fields An array of field definition objects, or field name strings.
15081 * @param {Object} config
15083 Roo.data.JsonStore = function(c){
15084 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15085 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15086 reader: new Roo.data.JsonReader(c, c.fields)
15089 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15091 * Ext JS Library 1.1.1
15092 * Copyright(c) 2006-2007, Ext JS, LLC.
15094 * Originally Released Under LGPL - original licence link has changed is not relivant.
15097 * <script type="text/javascript">
15101 Roo.data.Field = function(config){
15102 if(typeof config == "string"){
15103 config = {name: config};
15105 Roo.apply(this, config);
15108 this.type = "auto";
15111 var st = Roo.data.SortTypes;
15112 // named sortTypes are supported, here we look them up
15113 if(typeof this.sortType == "string"){
15114 this.sortType = st[this.sortType];
15117 // set default sortType for strings and dates
15118 if(!this.sortType){
15121 this.sortType = st.asUCString;
15124 this.sortType = st.asDate;
15127 this.sortType = st.none;
15132 var stripRe = /[\$,%]/g;
15134 // prebuilt conversion function for this field, instead of
15135 // switching every time we're reading a value
15137 var cv, dateFormat = this.dateFormat;
15142 cv = function(v){ return v; };
15145 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15149 return v !== undefined && v !== null && v !== '' ?
15150 parseInt(String(v).replace(stripRe, ""), 10) : '';
15155 return v !== undefined && v !== null && v !== '' ?
15156 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15161 cv = function(v){ return v === true || v === "true" || v == 1; };
15168 if(v instanceof Date){
15172 if(dateFormat == "timestamp"){
15173 return new Date(v*1000);
15175 return Date.parseDate(v, dateFormat);
15177 var parsed = Date.parse(v);
15178 return parsed ? new Date(parsed) : null;
15187 Roo.data.Field.prototype = {
15195 * Ext JS Library 1.1.1
15196 * Copyright(c) 2006-2007, Ext JS, LLC.
15198 * Originally Released Under LGPL - original licence link has changed is not relivant.
15201 * <script type="text/javascript">
15204 // Base class for reading structured data from a data source. This class is intended to be
15205 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15208 * @class Roo.data.DataReader
15209 * Base class for reading structured data from a data source. This class is intended to be
15210 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15213 Roo.data.DataReader = function(meta, recordType){
15217 this.recordType = recordType instanceof Array ?
15218 Roo.data.Record.create(recordType) : recordType;
15221 Roo.data.DataReader.prototype = {
15224 readerType : 'Data',
15226 * Create an empty record
15227 * @param {Object} data (optional) - overlay some values
15228 * @return {Roo.data.Record} record created.
15230 newRow : function(d) {
15232 this.recordType.prototype.fields.each(function(c) {
15234 case 'int' : da[c.name] = 0; break;
15235 case 'date' : da[c.name] = new Date(); break;
15236 case 'float' : da[c.name] = 0.0; break;
15237 case 'boolean' : da[c.name] = false; break;
15238 default : da[c.name] = ""; break;
15242 return new this.recordType(Roo.apply(da, d));
15248 * Ext JS Library 1.1.1
15249 * Copyright(c) 2006-2007, Ext JS, LLC.
15251 * Originally Released Under LGPL - original licence link has changed is not relivant.
15254 * <script type="text/javascript">
15258 * @class Roo.data.DataProxy
15259 * @extends Roo.data.Observable
15260 * This class is an abstract base class for implementations which provide retrieval of
15261 * unformatted data objects.<br>
15263 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15264 * (of the appropriate type which knows how to parse the data object) to provide a block of
15265 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15267 * Custom implementations must implement the load method as described in
15268 * {@link Roo.data.HttpProxy#load}.
15270 Roo.data.DataProxy = function(){
15273 * @event beforeload
15274 * Fires before a network request is made to retrieve a data object.
15275 * @param {Object} This DataProxy object.
15276 * @param {Object} params The params parameter to the load function.
15281 * Fires before the load method's callback is called.
15282 * @param {Object} This DataProxy object.
15283 * @param {Object} o The data object.
15284 * @param {Object} arg The callback argument object passed to the load function.
15288 * @event loadexception
15289 * Fires if an Exception occurs during data retrieval.
15290 * @param {Object} This DataProxy object.
15291 * @param {Object} o The data object.
15292 * @param {Object} arg The callback argument object passed to the load function.
15293 * @param {Object} e The Exception.
15295 loadexception : true
15297 Roo.data.DataProxy.superclass.constructor.call(this);
15300 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15303 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15307 * Ext JS Library 1.1.1
15308 * Copyright(c) 2006-2007, Ext JS, LLC.
15310 * Originally Released Under LGPL - original licence link has changed is not relivant.
15313 * <script type="text/javascript">
15316 * @class Roo.data.MemoryProxy
15317 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15318 * to the Reader when its load method is called.
15320 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15322 Roo.data.MemoryProxy = function(data){
15326 Roo.data.MemoryProxy.superclass.constructor.call(this);
15330 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15333 * Load data from the requested source (in this case an in-memory
15334 * data object passed to the constructor), read the data object into
15335 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15336 * process that block using the passed callback.
15337 * @param {Object} params This parameter is not used by the MemoryProxy class.
15338 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15339 * object into a block of Roo.data.Records.
15340 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15341 * The function must be passed <ul>
15342 * <li>The Record block object</li>
15343 * <li>The "arg" argument from the load function</li>
15344 * <li>A boolean success indicator</li>
15346 * @param {Object} scope The scope in which to call the callback
15347 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15349 load : function(params, reader, callback, scope, arg){
15350 params = params || {};
15353 result = reader.readRecords(params.data ? params.data :this.data);
15355 this.fireEvent("loadexception", this, arg, null, e);
15356 callback.call(scope, null, arg, false);
15359 callback.call(scope, result, arg, true);
15363 update : function(params, records){
15368 * Ext JS Library 1.1.1
15369 * Copyright(c) 2006-2007, Ext JS, LLC.
15371 * Originally Released Under LGPL - original licence link has changed is not relivant.
15374 * <script type="text/javascript">
15377 * @class Roo.data.HttpProxy
15378 * @extends Roo.data.DataProxy
15379 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15380 * configured to reference a certain URL.<br><br>
15382 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15383 * from which the running page was served.<br><br>
15385 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15387 * Be aware that to enable the browser to parse an XML document, the server must set
15388 * the Content-Type header in the HTTP response to "text/xml".
15390 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15391 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15392 * will be used to make the request.
15394 Roo.data.HttpProxy = function(conn){
15395 Roo.data.HttpProxy.superclass.constructor.call(this);
15396 // is conn a conn config or a real conn?
15398 this.useAjax = !conn || !conn.events;
15402 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15403 // thse are take from connection...
15406 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15409 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15410 * extra parameters to each request made by this object. (defaults to undefined)
15413 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15414 * to each request made by this object. (defaults to undefined)
15417 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
15420 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15423 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15429 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15433 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15434 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15435 * a finer-grained basis than the DataProxy events.
15437 getConnection : function(){
15438 return this.useAjax ? Roo.Ajax : this.conn;
15442 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15443 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15444 * process that block using the passed callback.
15445 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15446 * for the request to the remote server.
15447 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15448 * object into a block of Roo.data.Records.
15449 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15450 * The function must be passed <ul>
15451 * <li>The Record block object</li>
15452 * <li>The "arg" argument from the load function</li>
15453 * <li>A boolean success indicator</li>
15455 * @param {Object} scope The scope in which to call the callback
15456 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15458 load : function(params, reader, callback, scope, arg){
15459 if(this.fireEvent("beforeload", this, params) !== false){
15461 params : params || {},
15463 callback : callback,
15468 callback : this.loadResponse,
15472 Roo.applyIf(o, this.conn);
15473 if(this.activeRequest){
15474 Roo.Ajax.abort(this.activeRequest);
15476 this.activeRequest = Roo.Ajax.request(o);
15478 this.conn.request(o);
15481 callback.call(scope||this, null, arg, false);
15486 loadResponse : function(o, success, response){
15487 delete this.activeRequest;
15489 this.fireEvent("loadexception", this, o, response);
15490 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15495 result = o.reader.read(response);
15497 this.fireEvent("loadexception", this, o, response, e);
15498 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15502 this.fireEvent("load", this, o, o.request.arg);
15503 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15507 update : function(dataSet){
15512 updateResponse : function(dataSet){
15517 * Ext JS Library 1.1.1
15518 * Copyright(c) 2006-2007, Ext JS, LLC.
15520 * Originally Released Under LGPL - original licence link has changed is not relivant.
15523 * <script type="text/javascript">
15527 * @class Roo.data.ScriptTagProxy
15528 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15529 * other than the originating domain of the running page.<br><br>
15531 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
15532 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15534 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15535 * source code that is used as the source inside a <script> tag.<br><br>
15537 * In order for the browser to process the returned data, the server must wrap the data object
15538 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15539 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15540 * depending on whether the callback name was passed:
15543 boolean scriptTag = false;
15544 String cb = request.getParameter("callback");
15547 response.setContentType("text/javascript");
15549 response.setContentType("application/x-json");
15551 Writer out = response.getWriter();
15553 out.write(cb + "(");
15555 out.print(dataBlock.toJsonString());
15562 * @param {Object} config A configuration object.
15564 Roo.data.ScriptTagProxy = function(config){
15565 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15566 Roo.apply(this, config);
15567 this.head = document.getElementsByTagName("head")[0];
15570 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15572 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15574 * @cfg {String} url The URL from which to request the data object.
15577 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15581 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15582 * the server the name of the callback function set up by the load call to process the returned data object.
15583 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15584 * javascript output which calls this named function passing the data object as its only parameter.
15586 callbackParam : "callback",
15588 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15589 * name to the request.
15594 * Load data from the configured URL, read the data object into
15595 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15596 * process that block using the passed callback.
15597 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15598 * for the request to the remote server.
15599 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15600 * object into a block of Roo.data.Records.
15601 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15602 * The function must be passed <ul>
15603 * <li>The Record block object</li>
15604 * <li>The "arg" argument from the load function</li>
15605 * <li>A boolean success indicator</li>
15607 * @param {Object} scope The scope in which to call the callback
15608 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15610 load : function(params, reader, callback, scope, arg){
15611 if(this.fireEvent("beforeload", this, params) !== false){
15613 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15615 var url = this.url;
15616 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15618 url += "&_dc=" + (new Date().getTime());
15620 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15623 cb : "stcCallback"+transId,
15624 scriptId : "stcScript"+transId,
15628 callback : callback,
15634 window[trans.cb] = function(o){
15635 conn.handleResponse(o, trans);
15638 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15640 if(this.autoAbort !== false){
15644 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15646 var script = document.createElement("script");
15647 script.setAttribute("src", url);
15648 script.setAttribute("type", "text/javascript");
15649 script.setAttribute("id", trans.scriptId);
15650 this.head.appendChild(script);
15652 this.trans = trans;
15654 callback.call(scope||this, null, arg, false);
15659 isLoading : function(){
15660 return this.trans ? true : false;
15664 * Abort the current server request.
15666 abort : function(){
15667 if(this.isLoading()){
15668 this.destroyTrans(this.trans);
15673 destroyTrans : function(trans, isLoaded){
15674 this.head.removeChild(document.getElementById(trans.scriptId));
15675 clearTimeout(trans.timeoutId);
15677 window[trans.cb] = undefined;
15679 delete window[trans.cb];
15682 // if hasn't been loaded, wait for load to remove it to prevent script error
15683 window[trans.cb] = function(){
15684 window[trans.cb] = undefined;
15686 delete window[trans.cb];
15693 handleResponse : function(o, trans){
15694 this.trans = false;
15695 this.destroyTrans(trans, true);
15698 result = trans.reader.readRecords(o);
15700 this.fireEvent("loadexception", this, o, trans.arg, e);
15701 trans.callback.call(trans.scope||window, null, trans.arg, false);
15704 this.fireEvent("load", this, o, trans.arg);
15705 trans.callback.call(trans.scope||window, result, trans.arg, true);
15709 handleFailure : function(trans){
15710 this.trans = false;
15711 this.destroyTrans(trans, false);
15712 this.fireEvent("loadexception", this, null, trans.arg);
15713 trans.callback.call(trans.scope||window, null, trans.arg, false);
15717 * Ext JS Library 1.1.1
15718 * Copyright(c) 2006-2007, Ext JS, LLC.
15720 * Originally Released Under LGPL - original licence link has changed is not relivant.
15723 * <script type="text/javascript">
15727 * @class Roo.data.JsonReader
15728 * @extends Roo.data.DataReader
15729 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15730 * based on mappings in a provided Roo.data.Record constructor.
15732 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15733 * in the reply previously.
15738 var RecordDef = Roo.data.Record.create([
15739 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15740 {name: 'occupation'} // This field will use "occupation" as the mapping.
15742 var myReader = new Roo.data.JsonReader({
15743 totalProperty: "results", // The property which contains the total dataset size (optional)
15744 root: "rows", // The property which contains an Array of row objects
15745 id: "id" // The property within each row object that provides an ID for the record (optional)
15749 * This would consume a JSON file like this:
15751 { 'results': 2, 'rows': [
15752 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15753 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15756 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15757 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15758 * paged from the remote server.
15759 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15760 * @cfg {String} root name of the property which contains the Array of row objects.
15761 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15762 * @cfg {Array} fields Array of field definition objects
15764 * Create a new JsonReader
15765 * @param {Object} meta Metadata configuration options
15766 * @param {Object} recordType Either an Array of field definition objects,
15767 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15769 Roo.data.JsonReader = function(meta, recordType){
15772 // set some defaults:
15773 Roo.applyIf(meta, {
15774 totalProperty: 'total',
15775 successProperty : 'success',
15780 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15782 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15784 readerType : 'Json',
15787 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15788 * Used by Store query builder to append _requestMeta to params.
15791 metaFromRemote : false,
15793 * This method is only used by a DataProxy which has retrieved data from a remote server.
15794 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15795 * @return {Object} data A data block which is used by an Roo.data.Store object as
15796 * a cache of Roo.data.Records.
15798 read : function(response){
15799 var json = response.responseText;
15801 var o = /* eval:var:o */ eval("("+json+")");
15803 throw {message: "JsonReader.read: Json object not found"};
15809 this.metaFromRemote = true;
15810 this.meta = o.metaData;
15811 this.recordType = Roo.data.Record.create(o.metaData.fields);
15812 this.onMetaChange(this.meta, this.recordType, o);
15814 return this.readRecords(o);
15817 // private function a store will implement
15818 onMetaChange : function(meta, recordType, o){
15825 simpleAccess: function(obj, subsc) {
15832 getJsonAccessor: function(){
15834 return function(expr) {
15836 return(re.test(expr))
15837 ? new Function("obj", "return obj." + expr)
15842 return Roo.emptyFn;
15847 * Create a data block containing Roo.data.Records from an XML document.
15848 * @param {Object} o An object which contains an Array of row objects in the property specified
15849 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15850 * which contains the total size of the dataset.
15851 * @return {Object} data A data block which is used by an Roo.data.Store object as
15852 * a cache of Roo.data.Records.
15854 readRecords : function(o){
15856 * After any data loads, the raw JSON data is available for further custom processing.
15860 var s = this.meta, Record = this.recordType,
15861 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15863 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15865 if(s.totalProperty) {
15866 this.getTotal = this.getJsonAccessor(s.totalProperty);
15868 if(s.successProperty) {
15869 this.getSuccess = this.getJsonAccessor(s.successProperty);
15871 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15873 var g = this.getJsonAccessor(s.id);
15874 this.getId = function(rec) {
15876 return (r === undefined || r === "") ? null : r;
15879 this.getId = function(){return null;};
15882 for(var jj = 0; jj < fl; jj++){
15884 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15885 this.ef[jj] = this.getJsonAccessor(map);
15889 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15890 if(s.totalProperty){
15891 var vt = parseInt(this.getTotal(o), 10);
15896 if(s.successProperty){
15897 var vs = this.getSuccess(o);
15898 if(vs === false || vs === 'false'){
15903 for(var i = 0; i < c; i++){
15906 var id = this.getId(n);
15907 for(var j = 0; j < fl; j++){
15909 var v = this.ef[j](n);
15911 Roo.log('missing convert for ' + f.name);
15915 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15917 var record = new Record(values, id);
15919 records[i] = record;
15925 totalRecords : totalRecords
15928 // used when loading children.. @see loadDataFromChildren
15929 toLoadData: function(rec)
15931 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15932 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15933 return { data : data, total : data.length };
15938 * Ext JS Library 1.1.1
15939 * Copyright(c) 2006-2007, Ext JS, LLC.
15941 * Originally Released Under LGPL - original licence link has changed is not relivant.
15944 * <script type="text/javascript">
15948 * @class Roo.data.ArrayReader
15949 * @extends Roo.data.DataReader
15950 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15951 * Each element of that Array represents a row of data fields. The
15952 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15953 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15957 var RecordDef = Roo.data.Record.create([
15958 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15959 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15961 var myReader = new Roo.data.ArrayReader({
15962 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15966 * This would consume an Array like this:
15968 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15972 * Create a new JsonReader
15973 * @param {Object} meta Metadata configuration options.
15974 * @param {Object|Array} recordType Either an Array of field definition objects
15976 * @cfg {Array} fields Array of field definition objects
15977 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15978 * as specified to {@link Roo.data.Record#create},
15979 * or an {@link Roo.data.Record} object
15982 * created using {@link Roo.data.Record#create}.
15984 Roo.data.ArrayReader = function(meta, recordType)
15986 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15989 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15992 * Create a data block containing Roo.data.Records from an XML document.
15993 * @param {Object} o An Array of row objects which represents the dataset.
15994 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15995 * a cache of Roo.data.Records.
15997 readRecords : function(o)
15999 var sid = this.meta ? this.meta.id : null;
16000 var recordType = this.recordType, fields = recordType.prototype.fields;
16003 for(var i = 0; i < root.length; i++){
16006 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16007 for(var j = 0, jlen = fields.length; j < jlen; j++){
16008 var f = fields.items[j];
16009 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16010 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16012 values[f.name] = v;
16014 var record = new recordType(values, id);
16016 records[records.length] = record;
16020 totalRecords : records.length
16023 // used when loading children.. @see loadDataFromChildren
16024 toLoadData: function(rec)
16026 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16027 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16038 * @class Roo.bootstrap.ComboBox
16039 * @extends Roo.bootstrap.TriggerField
16040 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16041 * @cfg {Boolean} append (true|false) default false
16042 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16043 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16044 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16045 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16046 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16047 * @cfg {Boolean} animate default true
16048 * @cfg {Boolean} emptyResultText only for touch device
16049 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16050 * @cfg {String} emptyTitle default ''
16051 * @cfg {Number} width fixed with? experimental
16053 * Create a new ComboBox.
16054 * @param {Object} config Configuration options
16056 Roo.bootstrap.ComboBox = function(config){
16057 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16061 * Fires when the dropdown list is expanded
16062 * @param {Roo.bootstrap.ComboBox} combo This combo box
16067 * Fires when the dropdown list is collapsed
16068 * @param {Roo.bootstrap.ComboBox} combo This combo box
16072 * @event beforeselect
16073 * Fires before a list item is selected. Return false to cancel the selection.
16074 * @param {Roo.bootstrap.ComboBox} combo This combo box
16075 * @param {Roo.data.Record} record The data record returned from the underlying store
16076 * @param {Number} index The index of the selected item in the dropdown list
16078 'beforeselect' : true,
16081 * Fires when a list item is selected
16082 * @param {Roo.bootstrap.ComboBox} combo This combo box
16083 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16084 * @param {Number} index The index of the selected item in the dropdown list
16088 * @event beforequery
16089 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16090 * The event object passed has these properties:
16091 * @param {Roo.bootstrap.ComboBox} combo This combo box
16092 * @param {String} query The query
16093 * @param {Boolean} forceAll true to force "all" query
16094 * @param {Boolean} cancel true to cancel the query
16095 * @param {Object} e The query event object
16097 'beforequery': true,
16100 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16101 * @param {Roo.bootstrap.ComboBox} combo This combo box
16106 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16107 * @param {Roo.bootstrap.ComboBox} combo This combo box
16108 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16113 * Fires when the remove value from the combobox array
16114 * @param {Roo.bootstrap.ComboBox} combo This combo box
16118 * @event afterremove
16119 * Fires when the remove value from the combobox array
16120 * @param {Roo.bootstrap.ComboBox} combo This combo box
16122 'afterremove' : true,
16124 * @event specialfilter
16125 * Fires when specialfilter
16126 * @param {Roo.bootstrap.ComboBox} combo This combo box
16128 'specialfilter' : true,
16131 * Fires when tick the element
16132 * @param {Roo.bootstrap.ComboBox} combo This combo box
16136 * @event touchviewdisplay
16137 * Fires when touch view require special display (default is using displayField)
16138 * @param {Roo.bootstrap.ComboBox} combo This combo box
16139 * @param {Object} cfg set html .
16141 'touchviewdisplay' : true
16146 this.tickItems = [];
16148 this.selectedIndex = -1;
16149 if(this.mode == 'local'){
16150 if(config.queryDelay === undefined){
16151 this.queryDelay = 10;
16153 if(config.minChars === undefined){
16159 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16162 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16163 * rendering into an Roo.Editor, defaults to false)
16166 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16167 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16170 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16173 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16174 * the dropdown list (defaults to undefined, with no header element)
16178 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16182 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16184 listWidth: undefined,
16186 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16187 * mode = 'remote' or 'text' if mode = 'local')
16189 displayField: undefined,
16192 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16193 * mode = 'remote' or 'value' if mode = 'local').
16194 * Note: use of a valueField requires the user make a selection
16195 * in order for a value to be mapped.
16197 valueField: undefined,
16199 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16204 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16205 * field's data value (defaults to the underlying DOM element's name)
16207 hiddenName: undefined,
16209 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16213 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16215 selectedClass: 'active',
16218 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16222 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16223 * anchor positions (defaults to 'tl-bl')
16225 listAlign: 'tl-bl?',
16227 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16231 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16232 * query specified by the allQuery config option (defaults to 'query')
16234 triggerAction: 'query',
16236 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16237 * (defaults to 4, does not apply if editable = false)
16241 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16242 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16246 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16247 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16251 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16252 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16256 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16257 * when editable = true (defaults to false)
16259 selectOnFocus:false,
16261 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16263 queryParam: 'query',
16265 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16266 * when mode = 'remote' (defaults to 'Loading...')
16268 loadingText: 'Loading...',
16270 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16274 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16278 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16279 * traditional select (defaults to true)
16283 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16287 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16291 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16292 * listWidth has a higher value)
16296 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16297 * allow the user to set arbitrary text into the field (defaults to false)
16299 forceSelection:false,
16301 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16302 * if typeAhead = true (defaults to 250)
16304 typeAheadDelay : 250,
16306 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16307 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16309 valueNotFoundText : undefined,
16311 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16313 blockFocus : false,
16316 * @cfg {Boolean} disableClear Disable showing of clear button.
16318 disableClear : false,
16320 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16322 alwaysQuery : false,
16325 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16330 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16332 invalidClass : "has-warning",
16335 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16337 validClass : "has-success",
16340 * @cfg {Boolean} specialFilter (true|false) special filter default false
16342 specialFilter : false,
16345 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16347 mobileTouchView : true,
16350 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16352 useNativeIOS : false,
16355 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16357 mobile_restrict_height : false,
16359 ios_options : false,
16371 btnPosition : 'right',
16372 triggerList : true,
16373 showToggleBtn : true,
16375 emptyResultText: 'Empty',
16376 triggerText : 'Select',
16380 // element that contains real text value.. (when hidden is used..)
16382 getAutoCreate : function()
16387 * Render classic select for iso
16390 if(Roo.isIOS && this.useNativeIOS){
16391 cfg = this.getAutoCreateNativeIOS();
16399 if(Roo.isTouch && this.mobileTouchView){
16400 cfg = this.getAutoCreateTouchView();
16407 if(!this.tickable){
16408 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16413 * ComboBox with tickable selections
16416 var align = this.labelAlign || this.parentLabelAlign();
16419 cls : 'form-group roo-combobox-tickable' //input-group
16422 var btn_text_select = '';
16423 var btn_text_done = '';
16424 var btn_text_cancel = '';
16426 if (this.btn_text_show) {
16427 btn_text_select = 'Select';
16428 btn_text_done = 'Done';
16429 btn_text_cancel = 'Cancel';
16434 cls : 'tickable-buttons',
16439 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16440 //html : this.triggerText
16441 html: btn_text_select
16447 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16449 html: btn_text_done
16455 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16457 html: btn_text_cancel
16463 buttons.cn.unshift({
16465 cls: 'roo-select2-search-field-input'
16471 Roo.each(buttons.cn, function(c){
16473 c.cls += ' btn-' + _this.size;
16476 if (_this.disabled) {
16483 style : 'display: contents',
16488 cls: 'form-hidden-field'
16492 cls: 'roo-select2-choices',
16496 cls: 'roo-select2-search-field',
16507 cls: 'roo-select2-container input-group roo-select2-container-multi',
16513 // cls: 'typeahead typeahead-long dropdown-menu',
16514 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16519 if(this.hasFeedback && !this.allowBlank){
16523 cls: 'glyphicon form-control-feedback'
16526 combobox.cn.push(feedback);
16533 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16534 tooltip : 'This field is required'
16536 if (Roo.bootstrap.version == 4) {
16539 style : 'display:none'
16542 if (align ==='left' && this.fieldLabel.length) {
16544 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16551 cls : 'control-label col-form-label',
16552 html : this.fieldLabel
16564 var labelCfg = cfg.cn[1];
16565 var contentCfg = cfg.cn[2];
16568 if(this.indicatorpos == 'right'){
16574 cls : 'control-label col-form-label',
16578 html : this.fieldLabel
16594 labelCfg = cfg.cn[0];
16595 contentCfg = cfg.cn[1];
16599 if(this.labelWidth > 12){
16600 labelCfg.style = "width: " + this.labelWidth + 'px';
16602 if(this.width * 1 > 0){
16603 contentCfg.style = "width: " + this.width + 'px';
16605 if(this.labelWidth < 13 && this.labelmd == 0){
16606 this.labelmd = this.labelWidth;
16609 if(this.labellg > 0){
16610 labelCfg.cls += ' col-lg-' + this.labellg;
16611 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16614 if(this.labelmd > 0){
16615 labelCfg.cls += ' col-md-' + this.labelmd;
16616 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16619 if(this.labelsm > 0){
16620 labelCfg.cls += ' col-sm-' + this.labelsm;
16621 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16624 if(this.labelxs > 0){
16625 labelCfg.cls += ' col-xs-' + this.labelxs;
16626 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16630 } else if ( this.fieldLabel.length) {
16631 // Roo.log(" label");
16636 //cls : 'input-group-addon',
16637 html : this.fieldLabel
16642 if(this.indicatorpos == 'right'){
16646 //cls : 'input-group-addon',
16647 html : this.fieldLabel
16657 // Roo.log(" no label && no align");
16664 ['xs','sm','md','lg'].map(function(size){
16665 if (settings[size]) {
16666 cfg.cls += ' col-' + size + '-' + settings[size];
16674 _initEventsCalled : false,
16677 initEvents: function()
16679 if (this._initEventsCalled) { // as we call render... prevent looping...
16682 this._initEventsCalled = true;
16685 throw "can not find store for combo";
16688 this.indicator = this.indicatorEl();
16690 this.store = Roo.factory(this.store, Roo.data);
16691 this.store.parent = this;
16693 // if we are building from html. then this element is so complex, that we can not really
16694 // use the rendered HTML.
16695 // so we have to trash and replace the previous code.
16696 if (Roo.XComponent.build_from_html) {
16697 // remove this element....
16698 var e = this.el.dom, k=0;
16699 while (e ) { e = e.previousSibling; ++k;}
16704 this.rendered = false;
16706 this.render(this.parent().getChildContainer(true), k);
16709 if(Roo.isIOS && this.useNativeIOS){
16710 this.initIOSView();
16718 if(Roo.isTouch && this.mobileTouchView){
16719 this.initTouchView();
16724 this.initTickableEvents();
16728 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16730 if(this.hiddenName){
16732 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16734 this.hiddenField.dom.value =
16735 this.hiddenValue !== undefined ? this.hiddenValue :
16736 this.value !== undefined ? this.value : '';
16738 // prevent input submission
16739 this.el.dom.removeAttribute('name');
16740 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16745 // this.el.dom.setAttribute('autocomplete', 'off');
16748 var cls = 'x-combo-list';
16750 //this.list = new Roo.Layer({
16751 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16757 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16758 _this.list.setWidth(lw);
16761 this.list.on('mouseover', this.onViewOver, this);
16762 this.list.on('mousemove', this.onViewMove, this);
16763 this.list.on('scroll', this.onViewScroll, this);
16766 this.list.swallowEvent('mousewheel');
16767 this.assetHeight = 0;
16770 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16771 this.assetHeight += this.header.getHeight();
16774 this.innerList = this.list.createChild({cls:cls+'-inner'});
16775 this.innerList.on('mouseover', this.onViewOver, this);
16776 this.innerList.on('mousemove', this.onViewMove, this);
16777 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16779 if(this.allowBlank && !this.pageSize && !this.disableClear){
16780 this.footer = this.list.createChild({cls:cls+'-ft'});
16781 this.pageTb = new Roo.Toolbar(this.footer);
16785 this.footer = this.list.createChild({cls:cls+'-ft'});
16786 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16787 {pageSize: this.pageSize});
16791 if (this.pageTb && this.allowBlank && !this.disableClear) {
16793 this.pageTb.add(new Roo.Toolbar.Fill(), {
16794 cls: 'x-btn-icon x-btn-clear',
16796 handler: function()
16799 _this.clearValue();
16800 _this.onSelect(false, -1);
16805 this.assetHeight += this.footer.getHeight();
16810 this.tpl = Roo.bootstrap.version == 4 ?
16811 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16812 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16815 this.view = new Roo.View(this.list, this.tpl, {
16816 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16818 //this.view.wrapEl.setDisplayed(false);
16819 this.view.on('click', this.onViewClick, this);
16822 this.store.on('beforeload', this.onBeforeLoad, this);
16823 this.store.on('load', this.onLoad, this);
16824 this.store.on('loadexception', this.onLoadException, this);
16826 if(this.resizable){
16827 this.resizer = new Roo.Resizable(this.list, {
16828 pinned:true, handles:'se'
16830 this.resizer.on('resize', function(r, w, h){
16831 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16832 this.listWidth = w;
16833 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16834 this.restrictHeight();
16836 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16839 if(!this.editable){
16840 this.editable = true;
16841 this.setEditable(false);
16846 if (typeof(this.events.add.listeners) != 'undefined') {
16848 this.addicon = this.wrap.createChild(
16849 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16851 this.addicon.on('click', function(e) {
16852 this.fireEvent('add', this);
16855 if (typeof(this.events.edit.listeners) != 'undefined') {
16857 this.editicon = this.wrap.createChild(
16858 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16859 if (this.addicon) {
16860 this.editicon.setStyle('margin-left', '40px');
16862 this.editicon.on('click', function(e) {
16864 // we fire even if inothing is selected..
16865 this.fireEvent('edit', this, this.lastData );
16871 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16872 "up" : function(e){
16873 this.inKeyMode = true;
16877 "down" : function(e){
16878 if(!this.isExpanded()){
16879 this.onTriggerClick();
16881 this.inKeyMode = true;
16886 "enter" : function(e){
16887 // this.onViewClick();
16891 if(this.fireEvent("specialkey", this, e)){
16892 this.onViewClick(false);
16898 "esc" : function(e){
16902 "tab" : function(e){
16905 if(this.fireEvent("specialkey", this, e)){
16906 this.onViewClick(false);
16914 doRelay : function(foo, bar, hname){
16915 if(hname == 'down' || this.scope.isExpanded()){
16916 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16925 this.queryDelay = Math.max(this.queryDelay || 10,
16926 this.mode == 'local' ? 10 : 250);
16929 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16931 if(this.typeAhead){
16932 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16934 if(this.editable !== false){
16935 this.inputEl().on("keyup", this.onKeyUp, this);
16937 if(this.forceSelection){
16938 this.inputEl().on('blur', this.doForce, this);
16942 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16943 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16947 initTickableEvents: function()
16951 if(this.hiddenName){
16953 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16955 this.hiddenField.dom.value =
16956 this.hiddenValue !== undefined ? this.hiddenValue :
16957 this.value !== undefined ? this.value : '';
16959 // prevent input submission
16960 this.el.dom.removeAttribute('name');
16961 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16966 // this.list = this.el.select('ul.dropdown-menu',true).first();
16968 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16969 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16970 if(this.triggerList){
16971 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16974 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16975 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16977 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16978 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16980 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16981 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16983 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16984 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16985 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16988 this.cancelBtn.hide();
16993 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16994 _this.list.setWidth(lw);
16997 this.list.on('mouseover', this.onViewOver, this);
16998 this.list.on('mousemove', this.onViewMove, this);
17000 this.list.on('scroll', this.onViewScroll, this);
17003 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17004 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17007 this.view = new Roo.View(this.list, this.tpl, {
17012 selectedClass: this.selectedClass
17015 //this.view.wrapEl.setDisplayed(false);
17016 this.view.on('click', this.onViewClick, this);
17020 this.store.on('beforeload', this.onBeforeLoad, this);
17021 this.store.on('load', this.onLoad, this);
17022 this.store.on('loadexception', this.onLoadException, this);
17025 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17026 "up" : function(e){
17027 this.inKeyMode = true;
17031 "down" : function(e){
17032 this.inKeyMode = true;
17036 "enter" : function(e){
17037 if(this.fireEvent("specialkey", this, e)){
17038 this.onViewClick(false);
17044 "esc" : function(e){
17045 this.onTickableFooterButtonClick(e, false, false);
17048 "tab" : function(e){
17049 this.fireEvent("specialkey", this, e);
17051 this.onTickableFooterButtonClick(e, false, false);
17058 doRelay : function(e, fn, key){
17059 if(this.scope.isExpanded()){
17060 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17069 this.queryDelay = Math.max(this.queryDelay || 10,
17070 this.mode == 'local' ? 10 : 250);
17073 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17075 if(this.typeAhead){
17076 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17079 if(this.editable !== false){
17080 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17083 this.indicator = this.indicatorEl();
17085 if(this.indicator){
17086 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17087 this.indicator.hide();
17092 onDestroy : function(){
17094 this.view.setStore(null);
17095 this.view.el.removeAllListeners();
17096 this.view.el.remove();
17097 this.view.purgeListeners();
17100 this.list.dom.innerHTML = '';
17104 this.store.un('beforeload', this.onBeforeLoad, this);
17105 this.store.un('load', this.onLoad, this);
17106 this.store.un('loadexception', this.onLoadException, this);
17108 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17112 fireKey : function(e){
17113 if(e.isNavKeyPress() && !this.list.isVisible()){
17114 this.fireEvent("specialkey", this, e);
17119 onResize: function(w, h)
17123 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17125 // if(typeof w != 'number'){
17126 // // we do not handle it!?!?
17129 // var tw = this.trigger.getWidth();
17130 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17131 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17133 // this.inputEl().setWidth( this.adjustWidth('input', x));
17135 // //this.trigger.setStyle('left', x+'px');
17137 // if(this.list && this.listWidth === undefined){
17138 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17139 // this.list.setWidth(lw);
17140 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17148 * Allow or prevent the user from directly editing the field text. If false is passed,
17149 * the user will only be able to select from the items defined in the dropdown list. This method
17150 * is the runtime equivalent of setting the 'editable' config option at config time.
17151 * @param {Boolean} value True to allow the user to directly edit the field text
17153 setEditable : function(value){
17154 if(value == this.editable){
17157 this.editable = value;
17159 this.inputEl().dom.setAttribute('readOnly', true);
17160 this.inputEl().on('mousedown', this.onTriggerClick, this);
17161 this.inputEl().addClass('x-combo-noedit');
17163 this.inputEl().dom.removeAttribute('readOnly');
17164 this.inputEl().un('mousedown', this.onTriggerClick, this);
17165 this.inputEl().removeClass('x-combo-noedit');
17171 onBeforeLoad : function(combo,opts){
17172 if(!this.hasFocus){
17176 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17178 this.restrictHeight();
17179 this.selectedIndex = -1;
17183 onLoad : function(){
17185 this.hasQuery = false;
17187 if(!this.hasFocus){
17191 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17192 this.loading.hide();
17195 if(this.store.getCount() > 0){
17198 this.restrictHeight();
17199 if(this.lastQuery == this.allQuery){
17200 if(this.editable && !this.tickable){
17201 this.inputEl().dom.select();
17205 !this.selectByValue(this.value, true) &&
17208 !this.store.lastOptions ||
17209 typeof(this.store.lastOptions.add) == 'undefined' ||
17210 this.store.lastOptions.add != true
17213 this.select(0, true);
17216 if(this.autoFocus){
17219 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17220 this.taTask.delay(this.typeAheadDelay);
17224 this.onEmptyResults();
17230 onLoadException : function()
17232 this.hasQuery = false;
17234 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17235 this.loading.hide();
17238 if(this.tickable && this.editable){
17243 // only causes errors at present
17244 //Roo.log(this.store.reader.jsonData);
17245 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17247 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17253 onTypeAhead : function(){
17254 if(this.store.getCount() > 0){
17255 var r = this.store.getAt(0);
17256 var newValue = r.data[this.displayField];
17257 var len = newValue.length;
17258 var selStart = this.getRawValue().length;
17260 if(selStart != len){
17261 this.setRawValue(newValue);
17262 this.selectText(selStart, newValue.length);
17268 onSelect : function(record, index){
17270 if(this.fireEvent('beforeselect', this, record, index) !== false){
17272 this.setFromData(index > -1 ? record.data : false);
17275 this.fireEvent('select', this, record, index);
17280 * Returns the currently selected field value or empty string if no value is set.
17281 * @return {String} value The selected value
17283 getValue : function()
17285 if(Roo.isIOS && this.useNativeIOS){
17286 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17290 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17293 if(this.valueField){
17294 return typeof this.value != 'undefined' ? this.value : '';
17296 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17300 getRawValue : function()
17302 if(Roo.isIOS && this.useNativeIOS){
17303 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17306 var v = this.inputEl().getValue();
17312 * Clears any text/value currently set in the field
17314 clearValue : function(){
17316 if(this.hiddenField){
17317 this.hiddenField.dom.value = '';
17320 this.setRawValue('');
17321 this.lastSelectionText = '';
17322 this.lastData = false;
17324 var close = this.closeTriggerEl();
17335 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17336 * will be displayed in the field. If the value does not match the data value of an existing item,
17337 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17338 * Otherwise the field will be blank (although the value will still be set).
17339 * @param {String} value The value to match
17341 setValue : function(v)
17343 if(Roo.isIOS && this.useNativeIOS){
17344 this.setIOSValue(v);
17354 if(this.valueField){
17355 var r = this.findRecord(this.valueField, v);
17357 text = r.data[this.displayField];
17358 }else if(this.valueNotFoundText !== undefined){
17359 text = this.valueNotFoundText;
17362 this.lastSelectionText = text;
17363 if(this.hiddenField){
17364 this.hiddenField.dom.value = v;
17366 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17369 var close = this.closeTriggerEl();
17372 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17378 * @property {Object} the last set data for the element
17383 * Sets the value of the field based on a object which is related to the record format for the store.
17384 * @param {Object} value the value to set as. or false on reset?
17386 setFromData : function(o){
17393 var dv = ''; // display value
17394 var vv = ''; // value value..
17396 if (this.displayField) {
17397 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17399 // this is an error condition!!!
17400 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17403 if(this.valueField){
17404 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17407 var close = this.closeTriggerEl();
17410 if(dv.length || vv * 1 > 0){
17412 this.blockFocus=true;
17418 if(this.hiddenField){
17419 this.hiddenField.dom.value = vv;
17421 this.lastSelectionText = dv;
17422 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17426 // no hidden field.. - we store the value in 'value', but still display
17427 // display field!!!!
17428 this.lastSelectionText = dv;
17429 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17436 reset : function(){
17437 // overridden so that last data is reset..
17444 this.setValue(this.originalValue);
17445 //this.clearInvalid();
17446 this.lastData = false;
17448 this.view.clearSelections();
17454 findRecord : function(prop, value){
17456 if(this.store.getCount() > 0){
17457 this.store.each(function(r){
17458 if(r.data[prop] == value){
17468 getName: function()
17470 // returns hidden if it's set..
17471 if (!this.rendered) {return ''};
17472 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17476 onViewMove : function(e, t){
17477 this.inKeyMode = false;
17481 onViewOver : function(e, t){
17482 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17485 var item = this.view.findItemFromChild(t);
17488 var index = this.view.indexOf(item);
17489 this.select(index, false);
17494 onViewClick : function(view, doFocus, el, e)
17496 var index = this.view.getSelectedIndexes()[0];
17498 var r = this.store.getAt(index);
17502 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17509 Roo.each(this.tickItems, function(v,k){
17511 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17513 _this.tickItems.splice(k, 1);
17515 if(typeof(e) == 'undefined' && view == false){
17516 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17528 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17529 this.tickItems.push(r.data);
17532 if(typeof(e) == 'undefined' && view == false){
17533 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17540 this.onSelect(r, index);
17542 if(doFocus !== false && !this.blockFocus){
17543 this.inputEl().focus();
17548 restrictHeight : function(){
17549 //this.innerList.dom.style.height = '';
17550 //var inner = this.innerList.dom;
17551 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17552 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17553 //this.list.beginUpdate();
17554 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17555 this.list.alignTo(this.inputEl(), this.listAlign);
17556 this.list.alignTo(this.inputEl(), this.listAlign);
17557 //this.list.endUpdate();
17561 onEmptyResults : function(){
17563 if(this.tickable && this.editable){
17564 this.hasFocus = false;
17565 this.restrictHeight();
17573 * Returns true if the dropdown list is expanded, else false.
17575 isExpanded : function(){
17576 return this.list.isVisible();
17580 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17581 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17582 * @param {String} value The data value of the item to select
17583 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17584 * selected item if it is not currently in view (defaults to true)
17585 * @return {Boolean} True if the value matched an item in the list, else false
17587 selectByValue : function(v, scrollIntoView){
17588 if(v !== undefined && v !== null){
17589 var r = this.findRecord(this.valueField || this.displayField, v);
17591 this.select(this.store.indexOf(r), scrollIntoView);
17599 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17600 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17601 * @param {Number} index The zero-based index of the list item to select
17602 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17603 * selected item if it is not currently in view (defaults to true)
17605 select : function(index, scrollIntoView){
17606 this.selectedIndex = index;
17607 this.view.select(index);
17608 if(scrollIntoView !== false){
17609 var el = this.view.getNode(index);
17611 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17614 this.list.scrollChildIntoView(el, false);
17620 selectNext : function(){
17621 var ct = this.store.getCount();
17623 if(this.selectedIndex == -1){
17625 }else if(this.selectedIndex < ct-1){
17626 this.select(this.selectedIndex+1);
17632 selectPrev : function(){
17633 var ct = this.store.getCount();
17635 if(this.selectedIndex == -1){
17637 }else if(this.selectedIndex != 0){
17638 this.select(this.selectedIndex-1);
17644 onKeyUp : function(e){
17645 if(this.editable !== false && !e.isSpecialKey()){
17646 this.lastKey = e.getKey();
17647 this.dqTask.delay(this.queryDelay);
17652 validateBlur : function(){
17653 return !this.list || !this.list.isVisible();
17657 initQuery : function(){
17659 var v = this.getRawValue();
17661 if(this.tickable && this.editable){
17662 v = this.tickableInputEl().getValue();
17669 doForce : function(){
17670 if(this.inputEl().dom.value.length > 0){
17671 this.inputEl().dom.value =
17672 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17678 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17679 * query allowing the query action to be canceled if needed.
17680 * @param {String} query The SQL query to execute
17681 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17682 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17683 * saved in the current store (defaults to false)
17685 doQuery : function(q, forceAll){
17687 if(q === undefined || q === null){
17692 forceAll: forceAll,
17696 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17701 forceAll = qe.forceAll;
17702 if(forceAll === true || (q.length >= this.minChars)){
17704 this.hasQuery = true;
17706 if(this.lastQuery != q || this.alwaysQuery){
17707 this.lastQuery = q;
17708 if(this.mode == 'local'){
17709 this.selectedIndex = -1;
17711 this.store.clearFilter();
17714 if(this.specialFilter){
17715 this.fireEvent('specialfilter', this);
17720 this.store.filter(this.displayField, q);
17723 this.store.fireEvent("datachanged", this.store);
17730 this.store.baseParams[this.queryParam] = q;
17732 var options = {params : this.getParams(q)};
17735 options.add = true;
17736 options.params.start = this.page * this.pageSize;
17739 this.store.load(options);
17742 * this code will make the page width larger, at the beginning, the list not align correctly,
17743 * we should expand the list on onLoad
17744 * so command out it
17749 this.selectedIndex = -1;
17754 this.loadNext = false;
17758 getParams : function(q){
17760 //p[this.queryParam] = q;
17764 p.limit = this.pageSize;
17770 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17772 collapse : function(){
17773 if(!this.isExpanded()){
17779 this.hasFocus = false;
17783 this.cancelBtn.hide();
17784 this.trigger.show();
17787 this.tickableInputEl().dom.value = '';
17788 this.tickableInputEl().blur();
17793 Roo.get(document).un('mousedown', this.collapseIf, this);
17794 Roo.get(document).un('mousewheel', this.collapseIf, this);
17795 if (!this.editable) {
17796 Roo.get(document).un('keydown', this.listKeyPress, this);
17798 this.fireEvent('collapse', this);
17804 collapseIf : function(e){
17805 var in_combo = e.within(this.el);
17806 var in_list = e.within(this.list);
17807 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17809 if (in_combo || in_list || is_list) {
17810 //e.stopPropagation();
17815 this.onTickableFooterButtonClick(e, false, false);
17823 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17825 expand : function(){
17827 if(this.isExpanded() || !this.hasFocus){
17831 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17832 this.list.setWidth(lw);
17838 this.restrictHeight();
17842 this.tickItems = Roo.apply([], this.item);
17845 this.cancelBtn.show();
17846 this.trigger.hide();
17849 this.tickableInputEl().focus();
17854 Roo.get(document).on('mousedown', this.collapseIf, this);
17855 Roo.get(document).on('mousewheel', this.collapseIf, this);
17856 if (!this.editable) {
17857 Roo.get(document).on('keydown', this.listKeyPress, this);
17860 this.fireEvent('expand', this);
17864 // Implements the default empty TriggerField.onTriggerClick function
17865 onTriggerClick : function(e)
17867 Roo.log('trigger click');
17869 if(this.disabled || !this.triggerList){
17874 this.loadNext = false;
17876 if(this.isExpanded()){
17878 if (!this.blockFocus) {
17879 this.inputEl().focus();
17883 this.hasFocus = true;
17884 if(this.triggerAction == 'all') {
17885 this.doQuery(this.allQuery, true);
17887 this.doQuery(this.getRawValue());
17889 if (!this.blockFocus) {
17890 this.inputEl().focus();
17895 onTickableTriggerClick : function(e)
17902 this.loadNext = false;
17903 this.hasFocus = true;
17905 if(this.triggerAction == 'all') {
17906 this.doQuery(this.allQuery, true);
17908 this.doQuery(this.getRawValue());
17912 onSearchFieldClick : function(e)
17914 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17915 this.onTickableFooterButtonClick(e, false, false);
17919 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17924 this.loadNext = false;
17925 this.hasFocus = true;
17927 if(this.triggerAction == 'all') {
17928 this.doQuery(this.allQuery, true);
17930 this.doQuery(this.getRawValue());
17934 listKeyPress : function(e)
17936 //Roo.log('listkeypress');
17937 // scroll to first matching element based on key pres..
17938 if (e.isSpecialKey()) {
17941 var k = String.fromCharCode(e.getKey()).toUpperCase();
17944 var csel = this.view.getSelectedNodes();
17945 var cselitem = false;
17947 var ix = this.view.indexOf(csel[0]);
17948 cselitem = this.store.getAt(ix);
17949 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17955 this.store.each(function(v) {
17957 // start at existing selection.
17958 if (cselitem.id == v.id) {
17964 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17965 match = this.store.indexOf(v);
17971 if (match === false) {
17972 return true; // no more action?
17975 this.view.select(match);
17976 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17977 sn.scrollIntoView(sn.dom.parentNode, false);
17980 onViewScroll : function(e, t){
17982 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
17986 this.hasQuery = true;
17988 this.loading = this.list.select('.loading', true).first();
17990 if(this.loading === null){
17991 this.list.createChild({
17993 cls: 'loading roo-select2-more-results roo-select2-active',
17994 html: 'Loading more results...'
17997 this.loading = this.list.select('.loading', true).first();
17999 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18001 this.loading.hide();
18004 this.loading.show();
18009 this.loadNext = true;
18011 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18016 addItem : function(o)
18018 var dv = ''; // display value
18020 if (this.displayField) {
18021 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18023 // this is an error condition!!!
18024 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18031 var choice = this.choices.createChild({
18033 cls: 'roo-select2-search-choice',
18042 cls: 'roo-select2-search-choice-close fa fa-times',
18047 }, this.searchField);
18049 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18051 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18059 this.inputEl().dom.value = '';
18064 onRemoveItem : function(e, _self, o)
18066 e.preventDefault();
18068 this.lastItem = Roo.apply([], this.item);
18070 var index = this.item.indexOf(o.data) * 1;
18073 Roo.log('not this item?!');
18077 this.item.splice(index, 1);
18082 this.fireEvent('remove', this, e);
18088 syncValue : function()
18090 if(!this.item.length){
18097 Roo.each(this.item, function(i){
18098 if(_this.valueField){
18099 value.push(i[_this.valueField]);
18106 this.value = value.join(',');
18108 if(this.hiddenField){
18109 this.hiddenField.dom.value = this.value;
18112 this.store.fireEvent("datachanged", this.store);
18117 clearItem : function()
18119 if(!this.multiple){
18125 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18133 if(this.tickable && !Roo.isTouch){
18134 this.view.refresh();
18138 inputEl: function ()
18140 if(Roo.isIOS && this.useNativeIOS){
18141 return this.el.select('select.roo-ios-select', true).first();
18144 if(Roo.isTouch && this.mobileTouchView){
18145 return this.el.select('input.form-control',true).first();
18149 return this.searchField;
18152 return this.el.select('input.form-control',true).first();
18155 onTickableFooterButtonClick : function(e, btn, el)
18157 e.preventDefault();
18159 this.lastItem = Roo.apply([], this.item);
18161 if(btn && btn.name == 'cancel'){
18162 this.tickItems = Roo.apply([], this.item);
18171 Roo.each(this.tickItems, function(o){
18179 validate : function()
18181 if(this.getVisibilityEl().hasClass('hidden')){
18185 var v = this.getRawValue();
18188 v = this.getValue();
18191 if(this.disabled || this.allowBlank || v.length){
18196 this.markInvalid();
18200 tickableInputEl : function()
18202 if(!this.tickable || !this.editable){
18203 return this.inputEl();
18206 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18210 getAutoCreateTouchView : function()
18215 cls: 'form-group' //input-group
18221 type : this.inputType,
18222 cls : 'form-control x-combo-noedit',
18223 autocomplete: 'new-password',
18224 placeholder : this.placeholder || '',
18229 input.name = this.name;
18233 input.cls += ' input-' + this.size;
18236 if (this.disabled) {
18237 input.disabled = true;
18241 cls : 'roo-combobox-wrap',
18248 inputblock.cls += ' input-group';
18250 inputblock.cn.unshift({
18252 cls : 'input-group-addon input-group-prepend input-group-text',
18257 if(this.removable && !this.multiple){
18258 inputblock.cls += ' roo-removable';
18260 inputblock.cn.push({
18263 cls : 'roo-combo-removable-btn close'
18267 if(this.hasFeedback && !this.allowBlank){
18269 inputblock.cls += ' has-feedback';
18271 inputblock.cn.push({
18273 cls: 'glyphicon form-control-feedback'
18280 inputblock.cls += (this.before) ? '' : ' input-group';
18282 inputblock.cn.push({
18284 cls : 'input-group-addon input-group-append input-group-text',
18290 var ibwrap = inputblock;
18295 cls: 'roo-select2-choices',
18299 cls: 'roo-select2-search-field',
18312 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18317 cls: 'form-hidden-field'
18323 if(!this.multiple && this.showToggleBtn){
18329 if (this.caret != false) {
18332 cls: 'fa fa-' + this.caret
18339 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18341 Roo.bootstrap.version == 3 ? caret : '',
18344 cls: 'combobox-clear',
18358 combobox.cls += ' roo-select2-container-multi';
18361 var required = this.allowBlank ? {
18363 style: 'display: none'
18366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18367 tooltip : 'This field is required'
18370 var align = this.labelAlign || this.parentLabelAlign();
18372 if (align ==='left' && this.fieldLabel.length) {
18378 cls : 'control-label col-form-label',
18379 html : this.fieldLabel
18383 cls : 'roo-combobox-wrap ',
18390 var labelCfg = cfg.cn[1];
18391 var contentCfg = cfg.cn[2];
18394 if(this.indicatorpos == 'right'){
18399 cls : 'control-label col-form-label',
18403 html : this.fieldLabel
18409 cls : "roo-combobox-wrap ",
18417 labelCfg = cfg.cn[0];
18418 contentCfg = cfg.cn[1];
18423 if(this.labelWidth > 12){
18424 labelCfg.style = "width: " + this.labelWidth + 'px';
18427 if(this.labelWidth < 13 && this.labelmd == 0){
18428 this.labelmd = this.labelWidth;
18431 if(this.labellg > 0){
18432 labelCfg.cls += ' col-lg-' + this.labellg;
18433 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18436 if(this.labelmd > 0){
18437 labelCfg.cls += ' col-md-' + this.labelmd;
18438 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18441 if(this.labelsm > 0){
18442 labelCfg.cls += ' col-sm-' + this.labelsm;
18443 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18446 if(this.labelxs > 0){
18447 labelCfg.cls += ' col-xs-' + this.labelxs;
18448 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18452 } else if ( this.fieldLabel.length) {
18457 cls : 'control-label',
18458 html : this.fieldLabel
18469 if(this.indicatorpos == 'right'){
18473 cls : 'control-label',
18474 html : this.fieldLabel,
18492 var settings = this;
18494 ['xs','sm','md','lg'].map(function(size){
18495 if (settings[size]) {
18496 cfg.cls += ' col-' + size + '-' + settings[size];
18503 initTouchView : function()
18505 this.renderTouchView();
18507 this.touchViewEl.on('scroll', function(){
18508 this.el.dom.scrollTop = 0;
18511 this.originalValue = this.getValue();
18513 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18515 this.inputEl().on("click", this.showTouchView, this);
18516 if (this.triggerEl) {
18517 this.triggerEl.on("click", this.showTouchView, this);
18521 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18522 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18524 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18526 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18527 this.store.on('load', this.onTouchViewLoad, this);
18528 this.store.on('loadexception', this.onTouchViewLoadException, this);
18530 if(this.hiddenName){
18532 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18534 this.hiddenField.dom.value =
18535 this.hiddenValue !== undefined ? this.hiddenValue :
18536 this.value !== undefined ? this.value : '';
18538 this.el.dom.removeAttribute('name');
18539 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18543 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18544 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18547 if(this.removable && !this.multiple){
18548 var close = this.closeTriggerEl();
18550 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18551 close.on('click', this.removeBtnClick, this, close);
18555 * fix the bug in Safari iOS8
18557 this.inputEl().on("focus", function(e){
18558 document.activeElement.blur();
18561 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18568 renderTouchView : function()
18570 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18571 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18573 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18574 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18576 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18577 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18578 this.touchViewBodyEl.setStyle('overflow', 'auto');
18580 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18581 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18583 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18584 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18588 showTouchView : function()
18594 this.touchViewHeaderEl.hide();
18596 if(this.modalTitle.length){
18597 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18598 this.touchViewHeaderEl.show();
18601 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18602 this.touchViewEl.show();
18604 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18606 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18607 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18609 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18611 if(this.modalTitle.length){
18612 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18615 this.touchViewBodyEl.setHeight(bodyHeight);
18619 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18621 this.touchViewEl.addClass(['in','show']);
18624 if(this._touchViewMask){
18625 Roo.get(document.body).addClass("x-body-masked");
18626 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18627 this._touchViewMask.setStyle('z-index', 10000);
18628 this._touchViewMask.addClass('show');
18631 this.doTouchViewQuery();
18635 hideTouchView : function()
18637 this.touchViewEl.removeClass(['in','show']);
18641 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18643 this.touchViewEl.setStyle('display', 'none');
18646 if(this._touchViewMask){
18647 this._touchViewMask.removeClass('show');
18648 Roo.get(document.body).removeClass("x-body-masked");
18652 setTouchViewValue : function()
18659 Roo.each(this.tickItems, function(o){
18664 this.hideTouchView();
18667 doTouchViewQuery : function()
18676 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18680 if(!this.alwaysQuery || this.mode == 'local'){
18681 this.onTouchViewLoad();
18688 onTouchViewBeforeLoad : function(combo,opts)
18694 onTouchViewLoad : function()
18696 if(this.store.getCount() < 1){
18697 this.onTouchViewEmptyResults();
18701 this.clearTouchView();
18703 var rawValue = this.getRawValue();
18705 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18707 this.tickItems = [];
18709 this.store.data.each(function(d, rowIndex){
18710 var row = this.touchViewListGroup.createChild(template);
18712 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18713 row.addClass(d.data.cls);
18716 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18719 html : d.data[this.displayField]
18722 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18723 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18726 row.removeClass('selected');
18727 if(!this.multiple && this.valueField &&
18728 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18731 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18732 row.addClass('selected');
18735 if(this.multiple && this.valueField &&
18736 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18740 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18741 this.tickItems.push(d.data);
18744 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18748 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18750 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18752 if(this.modalTitle.length){
18753 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18756 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18758 if(this.mobile_restrict_height && listHeight < bodyHeight){
18759 this.touchViewBodyEl.setHeight(listHeight);
18764 if(firstChecked && listHeight > bodyHeight){
18765 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18770 onTouchViewLoadException : function()
18772 this.hideTouchView();
18775 onTouchViewEmptyResults : function()
18777 this.clearTouchView();
18779 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18781 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18785 clearTouchView : function()
18787 this.touchViewListGroup.dom.innerHTML = '';
18790 onTouchViewClick : function(e, el, o)
18792 e.preventDefault();
18795 var rowIndex = o.rowIndex;
18797 var r = this.store.getAt(rowIndex);
18799 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18801 if(!this.multiple){
18802 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18803 c.dom.removeAttribute('checked');
18806 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18808 this.setFromData(r.data);
18810 var close = this.closeTriggerEl();
18816 this.hideTouchView();
18818 this.fireEvent('select', this, r, rowIndex);
18823 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18824 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18825 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18829 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18830 this.addItem(r.data);
18831 this.tickItems.push(r.data);
18835 getAutoCreateNativeIOS : function()
18838 cls: 'form-group' //input-group,
18843 cls : 'roo-ios-select'
18847 combobox.name = this.name;
18850 if (this.disabled) {
18851 combobox.disabled = true;
18854 var settings = this;
18856 ['xs','sm','md','lg'].map(function(size){
18857 if (settings[size]) {
18858 cfg.cls += ' col-' + size + '-' + settings[size];
18868 initIOSView : function()
18870 this.store.on('load', this.onIOSViewLoad, this);
18875 onIOSViewLoad : function()
18877 if(this.store.getCount() < 1){
18881 this.clearIOSView();
18883 if(this.allowBlank) {
18885 var default_text = '-- SELECT --';
18887 if(this.placeholder.length){
18888 default_text = this.placeholder;
18891 if(this.emptyTitle.length){
18892 default_text += ' - ' + this.emptyTitle + ' -';
18895 var opt = this.inputEl().createChild({
18898 html : default_text
18902 o[this.valueField] = 0;
18903 o[this.displayField] = default_text;
18905 this.ios_options.push({
18912 this.store.data.each(function(d, rowIndex){
18916 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18917 html = d.data[this.displayField];
18922 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18923 value = d.data[this.valueField];
18932 if(this.value == d.data[this.valueField]){
18933 option['selected'] = true;
18936 var opt = this.inputEl().createChild(option);
18938 this.ios_options.push({
18945 this.inputEl().on('change', function(){
18946 this.fireEvent('select', this);
18951 clearIOSView: function()
18953 this.inputEl().dom.innerHTML = '';
18955 this.ios_options = [];
18958 setIOSValue: function(v)
18962 if(!this.ios_options){
18966 Roo.each(this.ios_options, function(opts){
18968 opts.el.dom.removeAttribute('selected');
18970 if(opts.data[this.valueField] != v){
18974 opts.el.dom.setAttribute('selected', true);
18980 * @cfg {Boolean} grow
18984 * @cfg {Number} growMin
18988 * @cfg {Number} growMax
18997 Roo.apply(Roo.bootstrap.ComboBox, {
19001 cls: 'modal-header',
19023 cls: 'list-group-item',
19027 cls: 'roo-combobox-list-group-item-value'
19031 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19045 listItemCheckbox : {
19047 cls: 'list-group-item',
19051 cls: 'roo-combobox-list-group-item-value'
19055 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19071 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19076 cls: 'modal-footer',
19084 cls: 'col-xs-6 text-left',
19087 cls: 'btn btn-danger roo-touch-view-cancel',
19093 cls: 'col-xs-6 text-right',
19096 cls: 'btn btn-success roo-touch-view-ok',
19107 Roo.apply(Roo.bootstrap.ComboBox, {
19109 touchViewTemplate : {
19111 cls: 'modal fade roo-combobox-touch-view',
19115 cls: 'modal-dialog',
19116 style : 'position:fixed', // we have to fix position....
19120 cls: 'modal-content',
19122 Roo.bootstrap.ComboBox.header,
19123 Roo.bootstrap.ComboBox.body,
19124 Roo.bootstrap.ComboBox.footer
19133 * Ext JS Library 1.1.1
19134 * Copyright(c) 2006-2007, Ext JS, LLC.
19136 * Originally Released Under LGPL - original licence link has changed is not relivant.
19139 * <script type="text/javascript">
19144 * @extends Roo.util.Observable
19145 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19146 * This class also supports single and multi selection modes. <br>
19147 * Create a data model bound view:
19149 var store = new Roo.data.Store(...);
19151 var view = new Roo.View({
19153 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19155 singleSelect: true,
19156 selectedClass: "ydataview-selected",
19160 // listen for node click?
19161 view.on("click", function(vw, index, node, e){
19162 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19166 dataModel.load("foobar.xml");
19168 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19170 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19171 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19173 * Note: old style constructor is still suported (container, template, config)
19176 * Create a new View
19177 * @param {Object} config The config object
19180 Roo.View = function(config, depreciated_tpl, depreciated_config){
19182 this.parent = false;
19184 if (typeof(depreciated_tpl) == 'undefined') {
19185 // new way.. - universal constructor.
19186 Roo.apply(this, config);
19187 this.el = Roo.get(this.el);
19190 this.el = Roo.get(config);
19191 this.tpl = depreciated_tpl;
19192 Roo.apply(this, depreciated_config);
19194 this.wrapEl = this.el.wrap().wrap();
19195 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19198 if(typeof(this.tpl) == "string"){
19199 this.tpl = new Roo.Template(this.tpl);
19201 // support xtype ctors..
19202 this.tpl = new Roo.factory(this.tpl, Roo);
19206 this.tpl.compile();
19211 * @event beforeclick
19212 * Fires before a click is processed. Returns false to cancel the default action.
19213 * @param {Roo.View} this
19214 * @param {Number} index The index of the target node
19215 * @param {HTMLElement} node The target node
19216 * @param {Roo.EventObject} e The raw event object
19218 "beforeclick" : true,
19221 * Fires when a template node is clicked.
19222 * @param {Roo.View} this
19223 * @param {Number} index The index of the target node
19224 * @param {HTMLElement} node The target node
19225 * @param {Roo.EventObject} e The raw event object
19230 * Fires when a template node is double clicked.
19231 * @param {Roo.View} this
19232 * @param {Number} index The index of the target node
19233 * @param {HTMLElement} node The target node
19234 * @param {Roo.EventObject} e The raw event object
19238 * @event contextmenu
19239 * Fires when a template node is right clicked.
19240 * @param {Roo.View} this
19241 * @param {Number} index The index of the target node
19242 * @param {HTMLElement} node The target node
19243 * @param {Roo.EventObject} e The raw event object
19245 "contextmenu" : true,
19247 * @event selectionchange
19248 * Fires when the selected nodes change.
19249 * @param {Roo.View} this
19250 * @param {Array} selections Array of the selected nodes
19252 "selectionchange" : true,
19255 * @event beforeselect
19256 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19257 * @param {Roo.View} this
19258 * @param {HTMLElement} node The node to be selected
19259 * @param {Array} selections Array of currently selected nodes
19261 "beforeselect" : true,
19263 * @event preparedata
19264 * Fires on every row to render, to allow you to change the data.
19265 * @param {Roo.View} this
19266 * @param {Object} data to be rendered (change this)
19268 "preparedata" : true
19276 "click": this.onClick,
19277 "dblclick": this.onDblClick,
19278 "contextmenu": this.onContextMenu,
19282 this.selections = [];
19284 this.cmp = new Roo.CompositeElementLite([]);
19286 this.store = Roo.factory(this.store, Roo.data);
19287 this.setStore(this.store, true);
19290 if ( this.footer && this.footer.xtype) {
19292 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19294 this.footer.dataSource = this.store;
19295 this.footer.container = fctr;
19296 this.footer = Roo.factory(this.footer, Roo);
19297 fctr.insertFirst(this.el);
19299 // this is a bit insane - as the paging toolbar seems to detach the el..
19300 // dom.parentNode.parentNode.parentNode
19301 // they get detached?
19305 Roo.View.superclass.constructor.call(this);
19310 Roo.extend(Roo.View, Roo.util.Observable, {
19313 * @cfg {Roo.data.Store} store Data store to load data from.
19318 * @cfg {String|Roo.Element} el The container element.
19323 * @cfg {String|Roo.Template} tpl The template used by this View
19327 * @cfg {String} dataName the named area of the template to use as the data area
19328 * Works with domtemplates roo-name="name"
19332 * @cfg {String} selectedClass The css class to add to selected nodes
19334 selectedClass : "x-view-selected",
19336 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19341 * @cfg {String} text to display on mask (default Loading)
19345 * @cfg {Boolean} multiSelect Allow multiple selection
19347 multiSelect : false,
19349 * @cfg {Boolean} singleSelect Allow single selection
19351 singleSelect: false,
19354 * @cfg {Boolean} toggleSelect - selecting
19356 toggleSelect : false,
19359 * @cfg {Boolean} tickable - selecting
19364 * Returns the element this view is bound to.
19365 * @return {Roo.Element}
19367 getEl : function(){
19368 return this.wrapEl;
19374 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19376 refresh : function(){
19377 //Roo.log('refresh');
19380 // if we are using something like 'domtemplate', then
19381 // the what gets used is:
19382 // t.applySubtemplate(NAME, data, wrapping data..)
19383 // the outer template then get' applied with
19384 // the store 'extra data'
19385 // and the body get's added to the
19386 // roo-name="data" node?
19387 // <span class='roo-tpl-{name}'></span> ?????
19391 this.clearSelections();
19392 this.el.update("");
19394 var records = this.store.getRange();
19395 if(records.length < 1) {
19397 // is this valid?? = should it render a template??
19399 this.el.update(this.emptyText);
19403 if (this.dataName) {
19404 this.el.update(t.apply(this.store.meta)); //????
19405 el = this.el.child('.roo-tpl-' + this.dataName);
19408 for(var i = 0, len = records.length; i < len; i++){
19409 var data = this.prepareData(records[i].data, i, records[i]);
19410 this.fireEvent("preparedata", this, data, i, records[i]);
19412 var d = Roo.apply({}, data);
19415 Roo.apply(d, {'roo-id' : Roo.id()});
19419 Roo.each(this.parent.item, function(item){
19420 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19423 Roo.apply(d, {'roo-data-checked' : 'checked'});
19427 html[html.length] = Roo.util.Format.trim(
19429 t.applySubtemplate(this.dataName, d, this.store.meta) :
19436 el.update(html.join(""));
19437 this.nodes = el.dom.childNodes;
19438 this.updateIndexes(0);
19443 * Function to override to reformat the data that is sent to
19444 * the template for each node.
19445 * DEPRICATED - use the preparedata event handler.
19446 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19447 * a JSON object for an UpdateManager bound view).
19449 prepareData : function(data, index, record)
19451 this.fireEvent("preparedata", this, data, index, record);
19455 onUpdate : function(ds, record){
19456 // Roo.log('on update');
19457 this.clearSelections();
19458 var index = this.store.indexOf(record);
19459 var n = this.nodes[index];
19460 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19461 n.parentNode.removeChild(n);
19462 this.updateIndexes(index, index);
19468 onAdd : function(ds, records, index)
19470 //Roo.log(['on Add', ds, records, index] );
19471 this.clearSelections();
19472 if(this.nodes.length == 0){
19476 var n = this.nodes[index];
19477 for(var i = 0, len = records.length; i < len; i++){
19478 var d = this.prepareData(records[i].data, i, records[i]);
19480 this.tpl.insertBefore(n, d);
19483 this.tpl.append(this.el, d);
19486 this.updateIndexes(index);
19489 onRemove : function(ds, record, index){
19490 // Roo.log('onRemove');
19491 this.clearSelections();
19492 var el = this.dataName ?
19493 this.el.child('.roo-tpl-' + this.dataName) :
19496 el.dom.removeChild(this.nodes[index]);
19497 this.updateIndexes(index);
19501 * Refresh an individual node.
19502 * @param {Number} index
19504 refreshNode : function(index){
19505 this.onUpdate(this.store, this.store.getAt(index));
19508 updateIndexes : function(startIndex, endIndex){
19509 var ns = this.nodes;
19510 startIndex = startIndex || 0;
19511 endIndex = endIndex || ns.length - 1;
19512 for(var i = startIndex; i <= endIndex; i++){
19513 ns[i].nodeIndex = i;
19518 * Changes the data store this view uses and refresh the view.
19519 * @param {Store} store
19521 setStore : function(store, initial){
19522 if(!initial && this.store){
19523 this.store.un("datachanged", this.refresh);
19524 this.store.un("add", this.onAdd);
19525 this.store.un("remove", this.onRemove);
19526 this.store.un("update", this.onUpdate);
19527 this.store.un("clear", this.refresh);
19528 this.store.un("beforeload", this.onBeforeLoad);
19529 this.store.un("load", this.onLoad);
19530 this.store.un("loadexception", this.onLoad);
19534 store.on("datachanged", this.refresh, this);
19535 store.on("add", this.onAdd, this);
19536 store.on("remove", this.onRemove, this);
19537 store.on("update", this.onUpdate, this);
19538 store.on("clear", this.refresh, this);
19539 store.on("beforeload", this.onBeforeLoad, this);
19540 store.on("load", this.onLoad, this);
19541 store.on("loadexception", this.onLoad, this);
19549 * onbeforeLoad - masks the loading area.
19552 onBeforeLoad : function(store,opts)
19554 //Roo.log('onBeforeLoad');
19556 this.el.update("");
19558 this.el.mask(this.mask ? this.mask : "Loading" );
19560 onLoad : function ()
19567 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19568 * @param {HTMLElement} node
19569 * @return {HTMLElement} The template node
19571 findItemFromChild : function(node){
19572 var el = this.dataName ?
19573 this.el.child('.roo-tpl-' + this.dataName,true) :
19576 if(!node || node.parentNode == el){
19579 var p = node.parentNode;
19580 while(p && p != el){
19581 if(p.parentNode == el){
19590 onClick : function(e){
19591 var item = this.findItemFromChild(e.getTarget());
19593 var index = this.indexOf(item);
19594 if(this.onItemClick(item, index, e) !== false){
19595 this.fireEvent("click", this, index, item, e);
19598 this.clearSelections();
19603 onContextMenu : function(e){
19604 var item = this.findItemFromChild(e.getTarget());
19606 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19611 onDblClick : function(e){
19612 var item = this.findItemFromChild(e.getTarget());
19614 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19618 onItemClick : function(item, index, e)
19620 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19623 if (this.toggleSelect) {
19624 var m = this.isSelected(item) ? 'unselect' : 'select';
19627 _t[m](item, true, false);
19630 if(this.multiSelect || this.singleSelect){
19631 if(this.multiSelect && e.shiftKey && this.lastSelection){
19632 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19634 this.select(item, this.multiSelect && e.ctrlKey);
19635 this.lastSelection = item;
19638 if(!this.tickable){
19639 e.preventDefault();
19647 * Get the number of selected nodes.
19650 getSelectionCount : function(){
19651 return this.selections.length;
19655 * Get the currently selected nodes.
19656 * @return {Array} An array of HTMLElements
19658 getSelectedNodes : function(){
19659 return this.selections;
19663 * Get the indexes of the selected nodes.
19666 getSelectedIndexes : function(){
19667 var indexes = [], s = this.selections;
19668 for(var i = 0, len = s.length; i < len; i++){
19669 indexes.push(s[i].nodeIndex);
19675 * Clear all selections
19676 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19678 clearSelections : function(suppressEvent){
19679 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19680 this.cmp.elements = this.selections;
19681 this.cmp.removeClass(this.selectedClass);
19682 this.selections = [];
19683 if(!suppressEvent){
19684 this.fireEvent("selectionchange", this, this.selections);
19690 * Returns true if the passed node is selected
19691 * @param {HTMLElement/Number} node The node or node index
19692 * @return {Boolean}
19694 isSelected : function(node){
19695 var s = this.selections;
19699 node = this.getNode(node);
19700 return s.indexOf(node) !== -1;
19705 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
19706 * @param {Boolean} keepExisting (optional) true to keep existing selections
19707 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19709 select : function(nodeInfo, keepExisting, suppressEvent){
19710 if(nodeInfo instanceof Array){
19712 this.clearSelections(true);
19714 for(var i = 0, len = nodeInfo.length; i < len; i++){
19715 this.select(nodeInfo[i], true, true);
19719 var node = this.getNode(nodeInfo);
19720 if(!node || this.isSelected(node)){
19721 return; // already selected.
19724 this.clearSelections(true);
19727 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19728 Roo.fly(node).addClass(this.selectedClass);
19729 this.selections.push(node);
19730 if(!suppressEvent){
19731 this.fireEvent("selectionchange", this, this.selections);
19739 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
19740 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19741 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19743 unselect : function(nodeInfo, keepExisting, suppressEvent)
19745 if(nodeInfo instanceof Array){
19746 Roo.each(this.selections, function(s) {
19747 this.unselect(s, nodeInfo);
19751 var node = this.getNode(nodeInfo);
19752 if(!node || !this.isSelected(node)){
19753 //Roo.log("not selected");
19754 return; // not selected.
19758 Roo.each(this.selections, function(s) {
19760 Roo.fly(node).removeClass(this.selectedClass);
19767 this.selections= ns;
19768 this.fireEvent("selectionchange", this, this.selections);
19772 * Gets a template node.
19773 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19774 * @return {HTMLElement} The node or null if it wasn't found
19776 getNode : function(nodeInfo){
19777 if(typeof nodeInfo == "string"){
19778 return document.getElementById(nodeInfo);
19779 }else if(typeof nodeInfo == "number"){
19780 return this.nodes[nodeInfo];
19786 * Gets a range template nodes.
19787 * @param {Number} startIndex
19788 * @param {Number} endIndex
19789 * @return {Array} An array of nodes
19791 getNodes : function(start, end){
19792 var ns = this.nodes;
19793 start = start || 0;
19794 end = typeof end == "undefined" ? ns.length - 1 : end;
19797 for(var i = start; i <= end; i++){
19801 for(var i = start; i >= end; i--){
19809 * Finds the index of the passed node
19810 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19811 * @return {Number} The index of the node or -1
19813 indexOf : function(node){
19814 node = this.getNode(node);
19815 if(typeof node.nodeIndex == "number"){
19816 return node.nodeIndex;
19818 var ns = this.nodes;
19819 for(var i = 0, len = ns.length; i < len; i++){
19830 * based on jquery fullcalendar
19834 Roo.bootstrap = Roo.bootstrap || {};
19836 * @class Roo.bootstrap.Calendar
19837 * @extends Roo.bootstrap.Component
19838 * Bootstrap Calendar class
19839 * @cfg {Boolean} loadMask (true|false) default false
19840 * @cfg {Object} header generate the user specific header of the calendar, default false
19843 * Create a new Container
19844 * @param {Object} config The config object
19849 Roo.bootstrap.Calendar = function(config){
19850 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19854 * Fires when a date is selected
19855 * @param {DatePicker} this
19856 * @param {Date} date The selected date
19860 * @event monthchange
19861 * Fires when the displayed month changes
19862 * @param {DatePicker} this
19863 * @param {Date} date The selected month
19865 'monthchange': true,
19867 * @event evententer
19868 * Fires when mouse over an event
19869 * @param {Calendar} this
19870 * @param {event} Event
19872 'evententer': true,
19874 * @event eventleave
19875 * Fires when the mouse leaves an
19876 * @param {Calendar} this
19879 'eventleave': true,
19881 * @event eventclick
19882 * Fires when the mouse click an
19883 * @param {Calendar} this
19892 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19895 * @cfg {Number} startDay
19896 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19904 getAutoCreate : function(){
19907 var fc_button = function(name, corner, style, content ) {
19908 return Roo.apply({},{
19910 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19912 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19915 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19926 style : 'width:100%',
19933 cls : 'fc-header-left',
19935 fc_button('prev', 'left', 'arrow', '‹' ),
19936 fc_button('next', 'right', 'arrow', '›' ),
19937 { tag: 'span', cls: 'fc-header-space' },
19938 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19946 cls : 'fc-header-center',
19950 cls: 'fc-header-title',
19953 html : 'month / year'
19961 cls : 'fc-header-right',
19963 /* fc_button('month', 'left', '', 'month' ),
19964 fc_button('week', '', '', 'week' ),
19965 fc_button('day', 'right', '', 'day' )
19977 header = this.header;
19980 var cal_heads = function() {
19982 // fixme - handle this.
19984 for (var i =0; i < Date.dayNames.length; i++) {
19985 var d = Date.dayNames[i];
19988 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19989 html : d.substring(0,3)
19993 ret[0].cls += ' fc-first';
19994 ret[6].cls += ' fc-last';
19997 var cal_cell = function(n) {
20000 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20005 cls: 'fc-day-number',
20009 cls: 'fc-day-content',
20013 style: 'position: relative;' // height: 17px;
20025 var cal_rows = function() {
20028 for (var r = 0; r < 6; r++) {
20035 for (var i =0; i < Date.dayNames.length; i++) {
20036 var d = Date.dayNames[i];
20037 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20040 row.cn[0].cls+=' fc-first';
20041 row.cn[0].cn[0].style = 'min-height:90px';
20042 row.cn[6].cls+=' fc-last';
20046 ret[0].cls += ' fc-first';
20047 ret[4].cls += ' fc-prev-last';
20048 ret[5].cls += ' fc-last';
20055 cls: 'fc-border-separate',
20056 style : 'width:100%',
20064 cls : 'fc-first fc-last',
20082 cls : 'fc-content',
20083 style : "position: relative;",
20086 cls : 'fc-view fc-view-month fc-grid',
20087 style : 'position: relative',
20088 unselectable : 'on',
20091 cls : 'fc-event-container',
20092 style : 'position:absolute;z-index:8;top:0;left:0;'
20110 initEvents : function()
20113 throw "can not find store for calendar";
20119 style: "text-align:center",
20123 style: "background-color:white;width:50%;margin:250 auto",
20127 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20138 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20140 var size = this.el.select('.fc-content', true).first().getSize();
20141 this.maskEl.setSize(size.width, size.height);
20142 this.maskEl.enableDisplayMode("block");
20143 if(!this.loadMask){
20144 this.maskEl.hide();
20147 this.store = Roo.factory(this.store, Roo.data);
20148 this.store.on('load', this.onLoad, this);
20149 this.store.on('beforeload', this.onBeforeLoad, this);
20153 this.cells = this.el.select('.fc-day',true);
20154 //Roo.log(this.cells);
20155 this.textNodes = this.el.query('.fc-day-number');
20156 this.cells.addClassOnOver('fc-state-hover');
20158 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20159 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20160 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20161 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20163 this.on('monthchange', this.onMonthChange, this);
20165 this.update(new Date().clearTime());
20168 resize : function() {
20169 var sz = this.el.getSize();
20171 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20172 this.el.select('.fc-day-content div',true).setHeight(34);
20177 showPrevMonth : function(e){
20178 this.update(this.activeDate.add("mo", -1));
20180 showToday : function(e){
20181 this.update(new Date().clearTime());
20184 showNextMonth : function(e){
20185 this.update(this.activeDate.add("mo", 1));
20189 showPrevYear : function(){
20190 this.update(this.activeDate.add("y", -1));
20194 showNextYear : function(){
20195 this.update(this.activeDate.add("y", 1));
20200 update : function(date)
20202 var vd = this.activeDate;
20203 this.activeDate = date;
20204 // if(vd && this.el){
20205 // var t = date.getTime();
20206 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20207 // Roo.log('using add remove');
20209 // this.fireEvent('monthchange', this, date);
20211 // this.cells.removeClass("fc-state-highlight");
20212 // this.cells.each(function(c){
20213 // if(c.dateValue == t){
20214 // c.addClass("fc-state-highlight");
20215 // setTimeout(function(){
20216 // try{c.dom.firstChild.focus();}catch(e){}
20226 var days = date.getDaysInMonth();
20228 var firstOfMonth = date.getFirstDateOfMonth();
20229 var startingPos = firstOfMonth.getDay()-this.startDay;
20231 if(startingPos < this.startDay){
20235 var pm = date.add(Date.MONTH, -1);
20236 var prevStart = pm.getDaysInMonth()-startingPos;
20238 this.cells = this.el.select('.fc-day',true);
20239 this.textNodes = this.el.query('.fc-day-number');
20240 this.cells.addClassOnOver('fc-state-hover');
20242 var cells = this.cells.elements;
20243 var textEls = this.textNodes;
20245 Roo.each(cells, function(cell){
20246 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20249 days += startingPos;
20251 // convert everything to numbers so it's fast
20252 var day = 86400000;
20253 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20256 //Roo.log(prevStart);
20258 var today = new Date().clearTime().getTime();
20259 var sel = date.clearTime().getTime();
20260 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20261 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20262 var ddMatch = this.disabledDatesRE;
20263 var ddText = this.disabledDatesText;
20264 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20265 var ddaysText = this.disabledDaysText;
20266 var format = this.format;
20268 var setCellClass = function(cal, cell){
20272 //Roo.log('set Cell Class');
20274 var t = d.getTime();
20278 cell.dateValue = t;
20280 cell.className += " fc-today";
20281 cell.className += " fc-state-highlight";
20282 cell.title = cal.todayText;
20285 // disable highlight in other month..
20286 //cell.className += " fc-state-highlight";
20291 cell.className = " fc-state-disabled";
20292 cell.title = cal.minText;
20296 cell.className = " fc-state-disabled";
20297 cell.title = cal.maxText;
20301 if(ddays.indexOf(d.getDay()) != -1){
20302 cell.title = ddaysText;
20303 cell.className = " fc-state-disabled";
20306 if(ddMatch && format){
20307 var fvalue = d.dateFormat(format);
20308 if(ddMatch.test(fvalue)){
20309 cell.title = ddText.replace("%0", fvalue);
20310 cell.className = " fc-state-disabled";
20314 if (!cell.initialClassName) {
20315 cell.initialClassName = cell.dom.className;
20318 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20323 for(; i < startingPos; i++) {
20324 textEls[i].innerHTML = (++prevStart);
20325 d.setDate(d.getDate()+1);
20327 cells[i].className = "fc-past fc-other-month";
20328 setCellClass(this, cells[i]);
20333 for(; i < days; i++){
20334 intDay = i - startingPos + 1;
20335 textEls[i].innerHTML = (intDay);
20336 d.setDate(d.getDate()+1);
20338 cells[i].className = ''; // "x-date-active";
20339 setCellClass(this, cells[i]);
20343 for(; i < 42; i++) {
20344 textEls[i].innerHTML = (++extraDays);
20345 d.setDate(d.getDate()+1);
20347 cells[i].className = "fc-future fc-other-month";
20348 setCellClass(this, cells[i]);
20351 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20353 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20355 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20356 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20358 if(totalRows != 6){
20359 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20360 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20363 this.fireEvent('monthchange', this, date);
20367 if(!this.internalRender){
20368 var main = this.el.dom.firstChild;
20369 var w = main.offsetWidth;
20370 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20371 Roo.fly(main).setWidth(w);
20372 this.internalRender = true;
20373 // opera does not respect the auto grow header center column
20374 // then, after it gets a width opera refuses to recalculate
20375 // without a second pass
20376 if(Roo.isOpera && !this.secondPass){
20377 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20378 this.secondPass = true;
20379 this.update.defer(10, this, [date]);
20386 findCell : function(dt) {
20387 dt = dt.clearTime().getTime();
20389 this.cells.each(function(c){
20390 //Roo.log("check " +c.dateValue + '?=' + dt);
20391 if(c.dateValue == dt){
20401 findCells : function(ev) {
20402 var s = ev.start.clone().clearTime().getTime();
20404 var e= ev.end.clone().clearTime().getTime();
20407 this.cells.each(function(c){
20408 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20410 if(c.dateValue > e){
20413 if(c.dateValue < s){
20422 // findBestRow: function(cells)
20426 // for (var i =0 ; i < cells.length;i++) {
20427 // ret = Math.max(cells[i].rows || 0,ret);
20434 addItem : function(ev)
20436 // look for vertical location slot in
20437 var cells = this.findCells(ev);
20439 // ev.row = this.findBestRow(cells);
20441 // work out the location.
20445 for(var i =0; i < cells.length; i++) {
20447 cells[i].row = cells[0].row;
20450 cells[i].row = cells[i].row + 1;
20460 if (crow.start.getY() == cells[i].getY()) {
20462 crow.end = cells[i];
20479 cells[0].events.push(ev);
20481 this.calevents.push(ev);
20484 clearEvents: function() {
20486 if(!this.calevents){
20490 Roo.each(this.cells.elements, function(c){
20496 Roo.each(this.calevents, function(e) {
20497 Roo.each(e.els, function(el) {
20498 el.un('mouseenter' ,this.onEventEnter, this);
20499 el.un('mouseleave' ,this.onEventLeave, this);
20504 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20510 renderEvents: function()
20514 this.cells.each(function(c) {
20523 if(c.row != c.events.length){
20524 r = 4 - (4 - (c.row - c.events.length));
20527 c.events = ev.slice(0, r);
20528 c.more = ev.slice(r);
20530 if(c.more.length && c.more.length == 1){
20531 c.events.push(c.more.pop());
20534 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20538 this.cells.each(function(c) {
20540 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20543 for (var e = 0; e < c.events.length; e++){
20544 var ev = c.events[e];
20545 var rows = ev.rows;
20547 for(var i = 0; i < rows.length; i++) {
20549 // how many rows should it span..
20552 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20553 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20555 unselectable : "on",
20558 cls: 'fc-event-inner',
20562 // cls: 'fc-event-time',
20563 // html : cells.length > 1 ? '' : ev.time
20567 cls: 'fc-event-title',
20568 html : String.format('{0}', ev.title)
20575 cls: 'ui-resizable-handle ui-resizable-e',
20576 html : '  '
20583 cfg.cls += ' fc-event-start';
20585 if ((i+1) == rows.length) {
20586 cfg.cls += ' fc-event-end';
20589 var ctr = _this.el.select('.fc-event-container',true).first();
20590 var cg = ctr.createChild(cfg);
20592 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20593 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20595 var r = (c.more.length) ? 1 : 0;
20596 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20597 cg.setWidth(ebox.right - sbox.x -2);
20599 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20600 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20601 cg.on('click', _this.onEventClick, _this, ev);
20612 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20613 style : 'position: absolute',
20614 unselectable : "on",
20617 cls: 'fc-event-inner',
20621 cls: 'fc-event-title',
20629 cls: 'ui-resizable-handle ui-resizable-e',
20630 html : '  '
20636 var ctr = _this.el.select('.fc-event-container',true).first();
20637 var cg = ctr.createChild(cfg);
20639 var sbox = c.select('.fc-day-content',true).first().getBox();
20640 var ebox = c.select('.fc-day-content',true).first().getBox();
20642 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20643 cg.setWidth(ebox.right - sbox.x -2);
20645 cg.on('click', _this.onMoreEventClick, _this, c.more);
20655 onEventEnter: function (e, el,event,d) {
20656 this.fireEvent('evententer', this, el, event);
20659 onEventLeave: function (e, el,event,d) {
20660 this.fireEvent('eventleave', this, el, event);
20663 onEventClick: function (e, el,event,d) {
20664 this.fireEvent('eventclick', this, el, event);
20667 onMonthChange: function () {
20671 onMoreEventClick: function(e, el, more)
20675 this.calpopover.placement = 'right';
20676 this.calpopover.setTitle('More');
20678 this.calpopover.setContent('');
20680 var ctr = this.calpopover.el.select('.popover-content', true).first();
20682 Roo.each(more, function(m){
20684 cls : 'fc-event-hori fc-event-draggable',
20687 var cg = ctr.createChild(cfg);
20689 cg.on('click', _this.onEventClick, _this, m);
20692 this.calpopover.show(el);
20697 onLoad: function ()
20699 this.calevents = [];
20702 if(this.store.getCount() > 0){
20703 this.store.data.each(function(d){
20706 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20707 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20708 time : d.data.start_time,
20709 title : d.data.title,
20710 description : d.data.description,
20711 venue : d.data.venue
20716 this.renderEvents();
20718 if(this.calevents.length && this.loadMask){
20719 this.maskEl.hide();
20723 onBeforeLoad: function()
20725 this.clearEvents();
20727 this.maskEl.show();
20741 * @class Roo.bootstrap.Popover
20742 * @extends Roo.bootstrap.Component
20743 * Bootstrap Popover class
20744 * @cfg {String} html contents of the popover (or false to use children..)
20745 * @cfg {String} title of popover (or false to hide)
20746 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20747 * @cfg {String} trigger click || hover (or false to trigger manually)
20748 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20749 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20750 * - if false and it has a 'parent' then it will be automatically added to that element
20751 * - if string - Roo.get will be called
20752 * @cfg {Number} delay - delay before showing
20755 * Create a new Popover
20756 * @param {Object} config The config object
20759 Roo.bootstrap.Popover = function(config){
20760 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20766 * After the popover show
20768 * @param {Roo.bootstrap.Popover} this
20773 * After the popover hide
20775 * @param {Roo.bootstrap.Popover} this
20781 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20786 placement : 'right',
20787 trigger : 'hover', // hover
20793 can_build_overlaid : false,
20795 maskEl : false, // the mask element
20798 alignEl : false, // when show is called with an element - this get's stored.
20800 getChildContainer : function()
20802 return this.contentEl;
20805 getPopoverHeader : function()
20807 this.title = true; // flag not to hide it..
20808 this.headerEl.addClass('p-0');
20809 return this.headerEl
20813 getAutoCreate : function(){
20816 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20817 style: 'display:block',
20823 cls : 'popover-inner ',
20827 cls: 'popover-title popover-header',
20828 html : this.title === false ? '' : this.title
20831 cls : 'popover-content popover-body ' + (this.cls || ''),
20832 html : this.html || ''
20843 * @param {string} the title
20845 setTitle: function(str)
20849 this.headerEl.dom.innerHTML = str;
20854 * @param {string} the body content
20856 setContent: function(str)
20859 if (this.contentEl) {
20860 this.contentEl.dom.innerHTML = str;
20864 // as it get's added to the bottom of the page.
20865 onRender : function(ct, position)
20867 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20872 var cfg = Roo.apply({}, this.getAutoCreate());
20876 cfg.cls += ' ' + this.cls;
20879 cfg.style = this.style;
20881 //Roo.log("adding to ");
20882 this.el = Roo.get(document.body).createChild(cfg, position);
20883 // Roo.log(this.el);
20886 this.contentEl = this.el.select('.popover-content',true).first();
20887 this.headerEl = this.el.select('.popover-title',true).first();
20890 if(typeof(this.items) != 'undefined'){
20891 var items = this.items;
20894 for(var i =0;i < items.length;i++) {
20895 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20899 this.items = nitems;
20901 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20902 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20909 resizeMask : function()
20911 this.maskEl.setSize(
20912 Roo.lib.Dom.getViewWidth(true),
20913 Roo.lib.Dom.getViewHeight(true)
20917 initEvents : function()
20921 Roo.bootstrap.Popover.register(this);
20924 this.arrowEl = this.el.select('.arrow',true).first();
20925 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20926 this.el.enableDisplayMode('block');
20930 if (this.over === false && !this.parent()) {
20933 if (this.triggers === false) {
20938 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20939 var triggers = this.trigger ? this.trigger.split(' ') : [];
20940 Roo.each(triggers, function(trigger) {
20942 if (trigger == 'click') {
20943 on_el.on('click', this.toggle, this);
20944 } else if (trigger != 'manual') {
20945 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20946 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20948 on_el.on(eventIn ,this.enter, this);
20949 on_el.on(eventOut, this.leave, this);
20959 toggle : function () {
20960 this.hoverState == 'in' ? this.leave() : this.enter();
20963 enter : function () {
20965 clearTimeout(this.timeout);
20967 this.hoverState = 'in';
20969 if (!this.delay || !this.delay.show) {
20974 this.timeout = setTimeout(function () {
20975 if (_t.hoverState == 'in') {
20978 }, this.delay.show)
20981 leave : function() {
20982 clearTimeout(this.timeout);
20984 this.hoverState = 'out';
20986 if (!this.delay || !this.delay.hide) {
20991 this.timeout = setTimeout(function () {
20992 if (_t.hoverState == 'out') {
20995 }, this.delay.hide)
20999 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21000 * @param {string} (left|right|top|bottom) position
21002 show : function (on_el, placement)
21004 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21005 on_el = on_el || false; // default to false
21008 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21009 on_el = this.parent().el;
21010 } else if (this.over) {
21011 on_el = Roo.get(this.over);
21016 this.alignEl = Roo.get( on_el );
21019 this.render(document.body);
21025 if (this.title === false) {
21026 this.headerEl.hide();
21031 this.el.dom.style.display = 'block';
21034 if (this.alignEl) {
21035 this.updatePosition(this.placement, true);
21038 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21039 var es = this.el.getSize();
21040 var x = Roo.lib.Dom.getViewWidth()/2;
21041 var y = Roo.lib.Dom.getViewHeight()/2;
21042 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21047 //var arrow = this.el.select('.arrow',true).first();
21048 //arrow.set(align[2],
21050 this.el.addClass('in');
21054 this.hoverState = 'in';
21057 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21058 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21059 this.maskEl.dom.style.display = 'block';
21060 this.maskEl.addClass('show');
21062 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21064 this.fireEvent('show', this);
21068 * fire this manually after loading a grid in the table for example
21069 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21070 * @param {Boolean} try and move it if we cant get right position.
21072 updatePosition : function(placement, try_move)
21074 // allow for calling with no parameters
21075 placement = placement ? placement : this.placement;
21076 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21078 this.el.removeClass([
21079 'fade','top','bottom', 'left', 'right','in',
21080 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21082 this.el.addClass(placement + ' bs-popover-' + placement);
21084 if (!this.alignEl ) {
21088 switch (placement) {
21090 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21091 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21092 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21093 //normal display... or moved up/down.
21094 this.el.setXY(offset);
21095 var xy = this.alignEl.getAnchorXY('tr', false);
21097 this.arrowEl.setXY(xy);
21100 // continue through...
21101 return this.updatePosition('left', false);
21105 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21106 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21107 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21108 //normal display... or moved up/down.
21109 this.el.setXY(offset);
21110 var xy = this.alignEl.getAnchorXY('tl', false);
21111 xy[0]-=10;xy[1]+=5; // << fix me
21112 this.arrowEl.setXY(xy);
21116 return this.updatePosition('right', false);
21119 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21120 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21121 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21122 //normal display... or moved up/down.
21123 this.el.setXY(offset);
21124 var xy = this.alignEl.getAnchorXY('t', false);
21125 xy[1]-=10; // << fix me
21126 this.arrowEl.setXY(xy);
21130 return this.updatePosition('bottom', false);
21133 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21134 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21135 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21136 //normal display... or moved up/down.
21137 this.el.setXY(offset);
21138 var xy = this.alignEl.getAnchorXY('b', false);
21139 xy[1]+=2; // << fix me
21140 this.arrowEl.setXY(xy);
21144 return this.updatePosition('top', false);
21155 this.el.setXY([0,0]);
21156 this.el.removeClass('in');
21158 this.hoverState = null;
21159 this.maskEl.hide(); // always..
21160 this.fireEvent('hide', this);
21166 Roo.apply(Roo.bootstrap.Popover, {
21169 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21170 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21171 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21172 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21177 clickHander : false,
21181 onMouseDown : function(e)
21183 if (this.popups.length && !e.getTarget(".roo-popover")) {
21184 /// what is nothing is showing..
21193 register : function(popup)
21195 if (!Roo.bootstrap.Popover.clickHandler) {
21196 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21198 // hide other popups.
21199 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21200 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21201 this.hideAll(); //<< why?
21202 //this.popups.push(popup);
21204 hideAll : function()
21206 this.popups.forEach(function(p) {
21210 onShow : function() {
21211 Roo.bootstrap.Popover.popups.push(this);
21213 onHide : function() {
21214 Roo.bootstrap.Popover.popups.remove(this);
21220 * Card header - holder for the card header elements.
21225 * @class Roo.bootstrap.PopoverNav
21226 * @extends Roo.bootstrap.NavGroup
21227 * Bootstrap Popover header navigation class
21229 * Create a new Popover Header Navigation
21230 * @param {Object} config The config object
21233 Roo.bootstrap.PopoverNav = function(config){
21234 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21237 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21240 container_method : 'getPopoverHeader'
21258 * @class Roo.bootstrap.Progress
21259 * @extends Roo.bootstrap.Component
21260 * Bootstrap Progress class
21261 * @cfg {Boolean} striped striped of the progress bar
21262 * @cfg {Boolean} active animated of the progress bar
21266 * Create a new Progress
21267 * @param {Object} config The config object
21270 Roo.bootstrap.Progress = function(config){
21271 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21274 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21279 getAutoCreate : function(){
21287 cfg.cls += ' progress-striped';
21291 cfg.cls += ' active';
21310 * @class Roo.bootstrap.ProgressBar
21311 * @extends Roo.bootstrap.Component
21312 * Bootstrap ProgressBar class
21313 * @cfg {Number} aria_valuenow aria-value now
21314 * @cfg {Number} aria_valuemin aria-value min
21315 * @cfg {Number} aria_valuemax aria-value max
21316 * @cfg {String} label label for the progress bar
21317 * @cfg {String} panel (success | info | warning | danger )
21318 * @cfg {String} role role of the progress bar
21319 * @cfg {String} sr_only text
21323 * Create a new ProgressBar
21324 * @param {Object} config The config object
21327 Roo.bootstrap.ProgressBar = function(config){
21328 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21331 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21335 aria_valuemax : 100,
21341 getAutoCreate : function()
21346 cls: 'progress-bar',
21347 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21359 cfg.role = this.role;
21362 if(this.aria_valuenow){
21363 cfg['aria-valuenow'] = this.aria_valuenow;
21366 if(this.aria_valuemin){
21367 cfg['aria-valuemin'] = this.aria_valuemin;
21370 if(this.aria_valuemax){
21371 cfg['aria-valuemax'] = this.aria_valuemax;
21374 if(this.label && !this.sr_only){
21375 cfg.html = this.label;
21379 cfg.cls += ' progress-bar-' + this.panel;
21385 update : function(aria_valuenow)
21387 this.aria_valuenow = aria_valuenow;
21389 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21404 * @class Roo.bootstrap.TabGroup
21405 * @extends Roo.bootstrap.Column
21406 * Bootstrap Column class
21407 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21408 * @cfg {Boolean} carousel true to make the group behave like a carousel
21409 * @cfg {Boolean} bullets show bullets for the panels
21410 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21411 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21412 * @cfg {Boolean} showarrow (true|false) show arrow default true
21415 * Create a new TabGroup
21416 * @param {Object} config The config object
21419 Roo.bootstrap.TabGroup = function(config){
21420 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21422 this.navId = Roo.id();
21425 Roo.bootstrap.TabGroup.register(this);
21429 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21432 transition : false,
21437 slideOnTouch : false,
21440 getAutoCreate : function()
21442 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21444 cfg.cls += ' tab-content';
21446 if (this.carousel) {
21447 cfg.cls += ' carousel slide';
21450 cls : 'carousel-inner',
21454 if(this.bullets && !Roo.isTouch){
21457 cls : 'carousel-bullets',
21461 if(this.bullets_cls){
21462 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21469 cfg.cn[0].cn.push(bullets);
21472 if(this.showarrow){
21473 cfg.cn[0].cn.push({
21475 class : 'carousel-arrow',
21479 class : 'carousel-prev',
21483 class : 'fa fa-chevron-left'
21489 class : 'carousel-next',
21493 class : 'fa fa-chevron-right'
21506 initEvents: function()
21508 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21509 // this.el.on("touchstart", this.onTouchStart, this);
21512 if(this.autoslide){
21515 this.slideFn = window.setInterval(function() {
21516 _this.showPanelNext();
21520 if(this.showarrow){
21521 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21522 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21528 // onTouchStart : function(e, el, o)
21530 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21534 // this.showPanelNext();
21538 getChildContainer : function()
21540 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21544 * register a Navigation item
21545 * @param {Roo.bootstrap.NavItem} the navitem to add
21547 register : function(item)
21549 this.tabs.push( item);
21550 item.navId = this.navId; // not really needed..
21555 getActivePanel : function()
21558 Roo.each(this.tabs, function(t) {
21568 getPanelByName : function(n)
21571 Roo.each(this.tabs, function(t) {
21572 if (t.tabId == n) {
21580 indexOfPanel : function(p)
21583 Roo.each(this.tabs, function(t,i) {
21584 if (t.tabId == p.tabId) {
21593 * show a specific panel
21594 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21595 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21597 showPanel : function (pan)
21599 if(this.transition || typeof(pan) == 'undefined'){
21600 Roo.log("waiting for the transitionend");
21604 if (typeof(pan) == 'number') {
21605 pan = this.tabs[pan];
21608 if (typeof(pan) == 'string') {
21609 pan = this.getPanelByName(pan);
21612 var cur = this.getActivePanel();
21615 Roo.log('pan or acitve pan is undefined');
21619 if (pan.tabId == this.getActivePanel().tabId) {
21623 if (false === cur.fireEvent('beforedeactivate')) {
21627 if(this.bullets > 0 && !Roo.isTouch){
21628 this.setActiveBullet(this.indexOfPanel(pan));
21631 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21633 //class="carousel-item carousel-item-next carousel-item-left"
21635 this.transition = true;
21636 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21637 var lr = dir == 'next' ? 'left' : 'right';
21638 pan.el.addClass(dir); // or prev
21639 pan.el.addClass('carousel-item-' + dir); // or prev
21640 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21641 cur.el.addClass(lr); // or right
21642 pan.el.addClass(lr);
21643 cur.el.addClass('carousel-item-' +lr); // or right
21644 pan.el.addClass('carousel-item-' +lr);
21648 cur.el.on('transitionend', function() {
21649 Roo.log("trans end?");
21651 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21652 pan.setActive(true);
21654 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21655 cur.setActive(false);
21657 _this.transition = false;
21659 }, this, { single: true } );
21664 cur.setActive(false);
21665 pan.setActive(true);
21670 showPanelNext : function()
21672 var i = this.indexOfPanel(this.getActivePanel());
21674 if (i >= this.tabs.length - 1 && !this.autoslide) {
21678 if (i >= this.tabs.length - 1 && this.autoslide) {
21682 this.showPanel(this.tabs[i+1]);
21685 showPanelPrev : function()
21687 var i = this.indexOfPanel(this.getActivePanel());
21689 if (i < 1 && !this.autoslide) {
21693 if (i < 1 && this.autoslide) {
21694 i = this.tabs.length;
21697 this.showPanel(this.tabs[i-1]);
21701 addBullet: function()
21703 if(!this.bullets || Roo.isTouch){
21706 var ctr = this.el.select('.carousel-bullets',true).first();
21707 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21708 var bullet = ctr.createChild({
21709 cls : 'bullet bullet-' + i
21710 },ctr.dom.lastChild);
21715 bullet.on('click', (function(e, el, o, ii, t){
21717 e.preventDefault();
21719 this.showPanel(ii);
21721 if(this.autoslide && this.slideFn){
21722 clearInterval(this.slideFn);
21723 this.slideFn = window.setInterval(function() {
21724 _this.showPanelNext();
21728 }).createDelegate(this, [i, bullet], true));
21733 setActiveBullet : function(i)
21739 Roo.each(this.el.select('.bullet', true).elements, function(el){
21740 el.removeClass('selected');
21743 var bullet = this.el.select('.bullet-' + i, true).first();
21749 bullet.addClass('selected');
21760 Roo.apply(Roo.bootstrap.TabGroup, {
21764 * register a Navigation Group
21765 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21767 register : function(navgrp)
21769 this.groups[navgrp.navId] = navgrp;
21773 * fetch a Navigation Group based on the navigation ID
21774 * if one does not exist , it will get created.
21775 * @param {string} the navgroup to add
21776 * @returns {Roo.bootstrap.NavGroup} the navgroup
21778 get: function(navId) {
21779 if (typeof(this.groups[navId]) == 'undefined') {
21780 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21782 return this.groups[navId] ;
21797 * @class Roo.bootstrap.TabPanel
21798 * @extends Roo.bootstrap.Component
21799 * Bootstrap TabPanel class
21800 * @cfg {Boolean} active panel active
21801 * @cfg {String} html panel content
21802 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21803 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21804 * @cfg {String} href click to link..
21805 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21809 * Create a new TabPanel
21810 * @param {Object} config The config object
21813 Roo.bootstrap.TabPanel = function(config){
21814 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21818 * Fires when the active status changes
21819 * @param {Roo.bootstrap.TabPanel} this
21820 * @param {Boolean} state the new state
21825 * @event beforedeactivate
21826 * Fires before a tab is de-activated - can be used to do validation on a form.
21827 * @param {Roo.bootstrap.TabPanel} this
21828 * @return {Boolean} false if there is an error
21831 'beforedeactivate': true
21834 this.tabId = this.tabId || Roo.id();
21838 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21845 touchSlide : false,
21846 getAutoCreate : function(){
21851 // item is needed for carousel - not sure if it has any effect otherwise
21852 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21853 html: this.html || ''
21857 cfg.cls += ' active';
21861 cfg.tabId = this.tabId;
21869 initEvents: function()
21871 var p = this.parent();
21873 this.navId = this.navId || p.navId;
21875 if (typeof(this.navId) != 'undefined') {
21876 // not really needed.. but just in case.. parent should be a NavGroup.
21877 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21881 var i = tg.tabs.length - 1;
21883 if(this.active && tg.bullets > 0 && i < tg.bullets){
21884 tg.setActiveBullet(i);
21888 this.el.on('click', this.onClick, this);
21890 if(Roo.isTouch && this.touchSlide){
21891 this.el.on("touchstart", this.onTouchStart, this);
21892 this.el.on("touchmove", this.onTouchMove, this);
21893 this.el.on("touchend", this.onTouchEnd, this);
21898 onRender : function(ct, position)
21900 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21903 setActive : function(state)
21905 Roo.log("panel - set active " + this.tabId + "=" + state);
21907 this.active = state;
21909 this.el.removeClass('active');
21911 } else if (!this.el.hasClass('active')) {
21912 this.el.addClass('active');
21915 this.fireEvent('changed', this, state);
21918 onClick : function(e)
21920 e.preventDefault();
21922 if(!this.href.length){
21926 window.location.href = this.href;
21935 onTouchStart : function(e)
21937 this.swiping = false;
21939 this.startX = e.browserEvent.touches[0].clientX;
21940 this.startY = e.browserEvent.touches[0].clientY;
21943 onTouchMove : function(e)
21945 this.swiping = true;
21947 this.endX = e.browserEvent.touches[0].clientX;
21948 this.endY = e.browserEvent.touches[0].clientY;
21951 onTouchEnd : function(e)
21958 var tabGroup = this.parent();
21960 if(this.endX > this.startX){ // swiping right
21961 tabGroup.showPanelPrev();
21965 if(this.startX > this.endX){ // swiping left
21966 tabGroup.showPanelNext();
21985 * @class Roo.bootstrap.DateField
21986 * @extends Roo.bootstrap.Input
21987 * Bootstrap DateField class
21988 * @cfg {Number} weekStart default 0
21989 * @cfg {String} viewMode default empty, (months|years)
21990 * @cfg {String} minViewMode default empty, (months|years)
21991 * @cfg {Number} startDate default -Infinity
21992 * @cfg {Number} endDate default Infinity
21993 * @cfg {Boolean} todayHighlight default false
21994 * @cfg {Boolean} todayBtn default false
21995 * @cfg {Boolean} calendarWeeks default false
21996 * @cfg {Object} daysOfWeekDisabled default empty
21997 * @cfg {Boolean} singleMode default false (true | false)
21999 * @cfg {Boolean} keyboardNavigation default true
22000 * @cfg {String} language default en
22003 * Create a new DateField
22004 * @param {Object} config The config object
22007 Roo.bootstrap.DateField = function(config){
22008 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22012 * Fires when this field show.
22013 * @param {Roo.bootstrap.DateField} this
22014 * @param {Mixed} date The date value
22019 * Fires when this field hide.
22020 * @param {Roo.bootstrap.DateField} this
22021 * @param {Mixed} date The date value
22026 * Fires when select a date.
22027 * @param {Roo.bootstrap.DateField} this
22028 * @param {Mixed} date The date value
22032 * @event beforeselect
22033 * Fires when before select a date.
22034 * @param {Roo.bootstrap.DateField} this
22035 * @param {Mixed} date The date value
22037 beforeselect : true
22041 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22044 * @cfg {String} format
22045 * The default date format string which can be overriden for localization support. The format must be
22046 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22050 * @cfg {String} altFormats
22051 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22052 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22054 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22062 todayHighlight : false,
22068 keyboardNavigation: true,
22070 calendarWeeks: false,
22072 startDate: -Infinity,
22076 daysOfWeekDisabled: [],
22080 singleMode : false,
22082 UTCDate: function()
22084 return new Date(Date.UTC.apply(Date, arguments));
22087 UTCToday: function()
22089 var today = new Date();
22090 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22093 getDate: function() {
22094 var d = this.getUTCDate();
22095 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22098 getUTCDate: function() {
22102 setDate: function(d) {
22103 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22106 setUTCDate: function(d) {
22108 this.setValue(this.formatDate(this.date));
22111 onRender: function(ct, position)
22114 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22116 this.language = this.language || 'en';
22117 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22118 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22120 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22121 this.format = this.format || 'm/d/y';
22122 this.isInline = false;
22123 this.isInput = true;
22124 this.component = this.el.select('.add-on', true).first() || false;
22125 this.component = (this.component && this.component.length === 0) ? false : this.component;
22126 this.hasInput = this.component && this.inputEl().length;
22128 if (typeof(this.minViewMode === 'string')) {
22129 switch (this.minViewMode) {
22131 this.minViewMode = 1;
22134 this.minViewMode = 2;
22137 this.minViewMode = 0;
22142 if (typeof(this.viewMode === 'string')) {
22143 switch (this.viewMode) {
22156 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22158 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22160 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22162 this.picker().on('mousedown', this.onMousedown, this);
22163 this.picker().on('click', this.onClick, this);
22165 this.picker().addClass('datepicker-dropdown');
22167 this.startViewMode = this.viewMode;
22169 if(this.singleMode){
22170 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22171 v.setVisibilityMode(Roo.Element.DISPLAY);
22175 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22176 v.setStyle('width', '189px');
22180 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22181 if(!this.calendarWeeks){
22186 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22187 v.attr('colspan', function(i, val){
22188 return parseInt(val) + 1;
22193 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22195 this.setStartDate(this.startDate);
22196 this.setEndDate(this.endDate);
22198 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22205 if(this.isInline) {
22210 picker : function()
22212 return this.pickerEl;
22213 // return this.el.select('.datepicker', true).first();
22216 fillDow: function()
22218 var dowCnt = this.weekStart;
22227 if(this.calendarWeeks){
22235 while (dowCnt < this.weekStart + 7) {
22239 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22243 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22246 fillMonths: function()
22249 var months = this.picker().select('>.datepicker-months td', true).first();
22251 months.dom.innerHTML = '';
22257 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22260 months.createChild(month);
22267 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
22269 if (this.date < this.startDate) {
22270 this.viewDate = new Date(this.startDate);
22271 } else if (this.date > this.endDate) {
22272 this.viewDate = new Date(this.endDate);
22274 this.viewDate = new Date(this.date);
22282 var d = new Date(this.viewDate),
22283 year = d.getUTCFullYear(),
22284 month = d.getUTCMonth(),
22285 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22286 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22287 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22288 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22289 currentDate = this.date && this.date.valueOf(),
22290 today = this.UTCToday();
22292 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22294 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22296 // this.picker.select('>tfoot th.today').
22297 // .text(dates[this.language].today)
22298 // .toggle(this.todayBtn !== false);
22300 this.updateNavArrows();
22303 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22305 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22307 prevMonth.setUTCDate(day);
22309 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22311 var nextMonth = new Date(prevMonth);
22313 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22315 nextMonth = nextMonth.valueOf();
22317 var fillMonths = false;
22319 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22321 while(prevMonth.valueOf() <= nextMonth) {
22324 if (prevMonth.getUTCDay() === this.weekStart) {
22326 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22334 if(this.calendarWeeks){
22335 // ISO 8601: First week contains first thursday.
22336 // ISO also states week starts on Monday, but we can be more abstract here.
22338 // Start of current week: based on weekstart/current date
22339 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22340 // Thursday of this week
22341 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22342 // First Thursday of year, year from thursday
22343 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22344 // Calendar week: ms between thursdays, div ms per day, div 7 days
22345 calWeek = (th - yth) / 864e5 / 7 + 1;
22347 fillMonths.cn.push({
22355 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22357 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22360 if (this.todayHighlight &&
22361 prevMonth.getUTCFullYear() == today.getFullYear() &&
22362 prevMonth.getUTCMonth() == today.getMonth() &&
22363 prevMonth.getUTCDate() == today.getDate()) {
22364 clsName += ' today';
22367 if (currentDate && prevMonth.valueOf() === currentDate) {
22368 clsName += ' active';
22371 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22372 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22373 clsName += ' disabled';
22376 fillMonths.cn.push({
22378 cls: 'day ' + clsName,
22379 html: prevMonth.getDate()
22382 prevMonth.setDate(prevMonth.getDate()+1);
22385 var currentYear = this.date && this.date.getUTCFullYear();
22386 var currentMonth = this.date && this.date.getUTCMonth();
22388 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22390 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22391 v.removeClass('active');
22393 if(currentYear === year && k === currentMonth){
22394 v.addClass('active');
22397 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22398 v.addClass('disabled');
22404 year = parseInt(year/10, 10) * 10;
22406 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22408 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22411 for (var i = -1; i < 11; i++) {
22412 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22414 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22422 showMode: function(dir)
22425 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22428 Roo.each(this.picker().select('>div',true).elements, function(v){
22429 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22432 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22437 if(this.isInline) {
22441 this.picker().removeClass(['bottom', 'top']);
22443 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22445 * place to the top of element!
22449 this.picker().addClass('top');
22450 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22455 this.picker().addClass('bottom');
22457 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22460 parseDate : function(value)
22462 if(!value || value instanceof Date){
22465 var v = Date.parseDate(value, this.format);
22466 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22467 v = Date.parseDate(value, 'Y-m-d');
22469 if(!v && this.altFormats){
22470 if(!this.altFormatsArray){
22471 this.altFormatsArray = this.altFormats.split("|");
22473 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22474 v = Date.parseDate(value, this.altFormatsArray[i]);
22480 formatDate : function(date, fmt)
22482 return (!date || !(date instanceof Date)) ?
22483 date : date.dateFormat(fmt || this.format);
22486 onFocus : function()
22488 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22492 onBlur : function()
22494 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22496 var d = this.inputEl().getValue();
22503 showPopup : function()
22505 this.picker().show();
22509 this.fireEvent('showpopup', this, this.date);
22512 hidePopup : function()
22514 if(this.isInline) {
22517 this.picker().hide();
22518 this.viewMode = this.startViewMode;
22521 this.fireEvent('hidepopup', this, this.date);
22525 onMousedown: function(e)
22527 e.stopPropagation();
22528 e.preventDefault();
22533 Roo.bootstrap.DateField.superclass.keyup.call(this);
22537 setValue: function(v)
22539 if(this.fireEvent('beforeselect', this, v) !== false){
22540 var d = new Date(this.parseDate(v) ).clearTime();
22542 if(isNaN(d.getTime())){
22543 this.date = this.viewDate = '';
22544 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22548 v = this.formatDate(d);
22550 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22552 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22556 this.fireEvent('select', this, this.date);
22560 getValue: function()
22562 return this.formatDate(this.date);
22565 fireKey: function(e)
22567 if (!this.picker().isVisible()){
22568 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22574 var dateChanged = false,
22576 newDate, newViewDate;
22581 e.preventDefault();
22585 if (!this.keyboardNavigation) {
22588 dir = e.keyCode == 37 ? -1 : 1;
22591 newDate = this.moveYear(this.date, dir);
22592 newViewDate = this.moveYear(this.viewDate, dir);
22593 } else if (e.shiftKey){
22594 newDate = this.moveMonth(this.date, dir);
22595 newViewDate = this.moveMonth(this.viewDate, dir);
22597 newDate = new Date(this.date);
22598 newDate.setUTCDate(this.date.getUTCDate() + dir);
22599 newViewDate = new Date(this.viewDate);
22600 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22602 if (this.dateWithinRange(newDate)){
22603 this.date = newDate;
22604 this.viewDate = newViewDate;
22605 this.setValue(this.formatDate(this.date));
22607 e.preventDefault();
22608 dateChanged = true;
22613 if (!this.keyboardNavigation) {
22616 dir = e.keyCode == 38 ? -1 : 1;
22618 newDate = this.moveYear(this.date, dir);
22619 newViewDate = this.moveYear(this.viewDate, dir);
22620 } else if (e.shiftKey){
22621 newDate = this.moveMonth(this.date, dir);
22622 newViewDate = this.moveMonth(this.viewDate, dir);
22624 newDate = new Date(this.date);
22625 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22626 newViewDate = new Date(this.viewDate);
22627 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22629 if (this.dateWithinRange(newDate)){
22630 this.date = newDate;
22631 this.viewDate = newViewDate;
22632 this.setValue(this.formatDate(this.date));
22634 e.preventDefault();
22635 dateChanged = true;
22639 this.setValue(this.formatDate(this.date));
22641 e.preventDefault();
22644 this.setValue(this.formatDate(this.date));
22658 onClick: function(e)
22660 e.stopPropagation();
22661 e.preventDefault();
22663 var target = e.getTarget();
22665 if(target.nodeName.toLowerCase() === 'i'){
22666 target = Roo.get(target).dom.parentNode;
22669 var nodeName = target.nodeName;
22670 var className = target.className;
22671 var html = target.innerHTML;
22672 //Roo.log(nodeName);
22674 switch(nodeName.toLowerCase()) {
22676 switch(className) {
22682 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22683 switch(this.viewMode){
22685 this.viewDate = this.moveMonth(this.viewDate, dir);
22689 this.viewDate = this.moveYear(this.viewDate, dir);
22695 var date = new Date();
22696 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22698 this.setValue(this.formatDate(this.date));
22705 if (className.indexOf('disabled') < 0) {
22706 if (!this.viewDate) {
22707 this.viewDate = new Date();
22709 this.viewDate.setUTCDate(1);
22710 if (className.indexOf('month') > -1) {
22711 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22713 var year = parseInt(html, 10) || 0;
22714 this.viewDate.setUTCFullYear(year);
22718 if(this.singleMode){
22719 this.setValue(this.formatDate(this.viewDate));
22730 //Roo.log(className);
22731 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22732 var day = parseInt(html, 10) || 1;
22733 var year = (this.viewDate || new Date()).getUTCFullYear(),
22734 month = (this.viewDate || new Date()).getUTCMonth();
22736 if (className.indexOf('old') > -1) {
22743 } else if (className.indexOf('new') > -1) {
22751 //Roo.log([year,month,day]);
22752 this.date = this.UTCDate(year, month, day,0,0,0,0);
22753 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22755 //Roo.log(this.formatDate(this.date));
22756 this.setValue(this.formatDate(this.date));
22763 setStartDate: function(startDate)
22765 this.startDate = startDate || -Infinity;
22766 if (this.startDate !== -Infinity) {
22767 this.startDate = this.parseDate(this.startDate);
22770 this.updateNavArrows();
22773 setEndDate: function(endDate)
22775 this.endDate = endDate || Infinity;
22776 if (this.endDate !== Infinity) {
22777 this.endDate = this.parseDate(this.endDate);
22780 this.updateNavArrows();
22783 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22785 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22786 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22787 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22789 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22790 return parseInt(d, 10);
22793 this.updateNavArrows();
22796 updateNavArrows: function()
22798 if(this.singleMode){
22802 var d = new Date(this.viewDate),
22803 year = d.getUTCFullYear(),
22804 month = d.getUTCMonth();
22806 Roo.each(this.picker().select('.prev', true).elements, function(v){
22808 switch (this.viewMode) {
22811 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22817 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22824 Roo.each(this.picker().select('.next', true).elements, function(v){
22826 switch (this.viewMode) {
22829 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22835 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22843 moveMonth: function(date, dir)
22848 var new_date = new Date(date.valueOf()),
22849 day = new_date.getUTCDate(),
22850 month = new_date.getUTCMonth(),
22851 mag = Math.abs(dir),
22853 dir = dir > 0 ? 1 : -1;
22856 // If going back one month, make sure month is not current month
22857 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22859 return new_date.getUTCMonth() == month;
22861 // If going forward one month, make sure month is as expected
22862 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22864 return new_date.getUTCMonth() != new_month;
22866 new_month = month + dir;
22867 new_date.setUTCMonth(new_month);
22868 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22869 if (new_month < 0 || new_month > 11) {
22870 new_month = (new_month + 12) % 12;
22873 // For magnitudes >1, move one month at a time...
22874 for (var i=0; i<mag; i++) {
22875 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22876 new_date = this.moveMonth(new_date, dir);
22878 // ...then reset the day, keeping it in the new month
22879 new_month = new_date.getUTCMonth();
22880 new_date.setUTCDate(day);
22882 return new_month != new_date.getUTCMonth();
22885 // Common date-resetting loop -- if date is beyond end of month, make it
22888 new_date.setUTCDate(--day);
22889 new_date.setUTCMonth(new_month);
22894 moveYear: function(date, dir)
22896 return this.moveMonth(date, dir*12);
22899 dateWithinRange: function(date)
22901 return date >= this.startDate && date <= this.endDate;
22907 this.picker().remove();
22910 validateValue : function(value)
22912 if(this.getVisibilityEl().hasClass('hidden')){
22916 if(value.length < 1) {
22917 if(this.allowBlank){
22923 if(value.length < this.minLength){
22926 if(value.length > this.maxLength){
22930 var vt = Roo.form.VTypes;
22931 if(!vt[this.vtype](value, this)){
22935 if(typeof this.validator == "function"){
22936 var msg = this.validator(value);
22942 if(this.regex && !this.regex.test(value)){
22946 if(typeof(this.parseDate(value)) == 'undefined'){
22950 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22954 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22964 this.date = this.viewDate = '';
22966 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22971 Roo.apply(Roo.bootstrap.DateField, {
22982 html: '<i class="fa fa-arrow-left"/>'
22992 html: '<i class="fa fa-arrow-right"/>'
23034 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23035 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23036 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23037 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23038 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23051 navFnc: 'FullYear',
23056 navFnc: 'FullYear',
23061 Roo.apply(Roo.bootstrap.DateField, {
23065 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23069 cls: 'datepicker-days',
23073 cls: 'table-condensed',
23075 Roo.bootstrap.DateField.head,
23079 Roo.bootstrap.DateField.footer
23086 cls: 'datepicker-months',
23090 cls: 'table-condensed',
23092 Roo.bootstrap.DateField.head,
23093 Roo.bootstrap.DateField.content,
23094 Roo.bootstrap.DateField.footer
23101 cls: 'datepicker-years',
23105 cls: 'table-condensed',
23107 Roo.bootstrap.DateField.head,
23108 Roo.bootstrap.DateField.content,
23109 Roo.bootstrap.DateField.footer
23128 * @class Roo.bootstrap.TimeField
23129 * @extends Roo.bootstrap.Input
23130 * Bootstrap DateField class
23134 * Create a new TimeField
23135 * @param {Object} config The config object
23138 Roo.bootstrap.TimeField = function(config){
23139 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23143 * Fires when this field show.
23144 * @param {Roo.bootstrap.DateField} thisthis
23145 * @param {Mixed} date The date value
23150 * Fires when this field hide.
23151 * @param {Roo.bootstrap.DateField} this
23152 * @param {Mixed} date The date value
23157 * Fires when select a date.
23158 * @param {Roo.bootstrap.DateField} this
23159 * @param {Mixed} date The date value
23165 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23168 * @cfg {String} format
23169 * The default time format string which can be overriden for localization support. The format must be
23170 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23174 getAutoCreate : function()
23176 this.after = '<i class="fa far fa-clock"></i>';
23177 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23181 onRender: function(ct, position)
23184 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23186 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23188 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23190 this.pop = this.picker().select('>.datepicker-time',true).first();
23191 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23193 this.picker().on('mousedown', this.onMousedown, this);
23194 this.picker().on('click', this.onClick, this);
23196 this.picker().addClass('datepicker-dropdown');
23201 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23202 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23203 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23204 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23205 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23206 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23210 fireKey: function(e){
23211 if (!this.picker().isVisible()){
23212 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23218 e.preventDefault();
23226 this.onTogglePeriod();
23229 this.onIncrementMinutes();
23232 this.onDecrementMinutes();
23241 onClick: function(e) {
23242 e.stopPropagation();
23243 e.preventDefault();
23246 picker : function()
23248 return this.pickerEl;
23251 fillTime: function()
23253 var time = this.pop.select('tbody', true).first();
23255 time.dom.innerHTML = '';
23270 cls: 'hours-up fa fas fa-chevron-up'
23290 cls: 'minutes-up fa fas fa-chevron-up'
23311 cls: 'timepicker-hour',
23326 cls: 'timepicker-minute',
23341 cls: 'btn btn-primary period',
23363 cls: 'hours-down fa fas fa-chevron-down'
23383 cls: 'minutes-down fa fas fa-chevron-down'
23401 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23408 var hours = this.time.getHours();
23409 var minutes = this.time.getMinutes();
23422 hours = hours - 12;
23426 hours = '0' + hours;
23430 minutes = '0' + minutes;
23433 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23434 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23435 this.pop.select('button', true).first().dom.innerHTML = period;
23441 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23443 var cls = ['bottom'];
23445 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23452 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23456 //this.picker().setXY(20000,20000);
23457 this.picker().addClass(cls.join('-'));
23461 Roo.each(cls, function(c){
23466 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23467 //_this.picker().setTop(_this.inputEl().getHeight());
23471 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23473 //_this.picker().setTop(0 - _this.picker().getHeight());
23478 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23482 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23490 onFocus : function()
23492 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23496 onBlur : function()
23498 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23504 this.picker().show();
23509 this.fireEvent('show', this, this.date);
23514 this.picker().hide();
23517 this.fireEvent('hide', this, this.date);
23520 setTime : function()
23523 this.setValue(this.time.format(this.format));
23525 this.fireEvent('select', this, this.date);
23530 onMousedown: function(e){
23531 e.stopPropagation();
23532 e.preventDefault();
23535 onIncrementHours: function()
23537 Roo.log('onIncrementHours');
23538 this.time = this.time.add(Date.HOUR, 1);
23543 onDecrementHours: function()
23545 Roo.log('onDecrementHours');
23546 this.time = this.time.add(Date.HOUR, -1);
23550 onIncrementMinutes: function()
23552 Roo.log('onIncrementMinutes');
23553 this.time = this.time.add(Date.MINUTE, 1);
23557 onDecrementMinutes: function()
23559 Roo.log('onDecrementMinutes');
23560 this.time = this.time.add(Date.MINUTE, -1);
23564 onTogglePeriod: function()
23566 Roo.log('onTogglePeriod');
23567 this.time = this.time.add(Date.HOUR, 12);
23575 Roo.apply(Roo.bootstrap.TimeField, {
23579 cls: 'datepicker dropdown-menu',
23583 cls: 'datepicker-time',
23587 cls: 'table-condensed',
23616 cls: 'btn btn-info ok',
23644 * @class Roo.bootstrap.MonthField
23645 * @extends Roo.bootstrap.Input
23646 * Bootstrap MonthField class
23648 * @cfg {String} language default en
23651 * Create a new MonthField
23652 * @param {Object} config The config object
23655 Roo.bootstrap.MonthField = function(config){
23656 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23661 * Fires when this field show.
23662 * @param {Roo.bootstrap.MonthField} this
23663 * @param {Mixed} date The date value
23668 * Fires when this field hide.
23669 * @param {Roo.bootstrap.MonthField} this
23670 * @param {Mixed} date The date value
23675 * Fires when select a date.
23676 * @param {Roo.bootstrap.MonthField} this
23677 * @param {String} oldvalue The old value
23678 * @param {String} newvalue The new value
23684 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23686 onRender: function(ct, position)
23689 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23691 this.language = this.language || 'en';
23692 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23693 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23695 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23696 this.isInline = false;
23697 this.isInput = true;
23698 this.component = this.el.select('.add-on', true).first() || false;
23699 this.component = (this.component && this.component.length === 0) ? false : this.component;
23700 this.hasInput = this.component && this.inputEL().length;
23702 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23704 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23706 this.picker().on('mousedown', this.onMousedown, this);
23707 this.picker().on('click', this.onClick, this);
23709 this.picker().addClass('datepicker-dropdown');
23711 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23712 v.setStyle('width', '189px');
23719 if(this.isInline) {
23725 setValue: function(v, suppressEvent)
23727 var o = this.getValue();
23729 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23733 if(suppressEvent !== true){
23734 this.fireEvent('select', this, o, v);
23739 getValue: function()
23744 onClick: function(e)
23746 e.stopPropagation();
23747 e.preventDefault();
23749 var target = e.getTarget();
23751 if(target.nodeName.toLowerCase() === 'i'){
23752 target = Roo.get(target).dom.parentNode;
23755 var nodeName = target.nodeName;
23756 var className = target.className;
23757 var html = target.innerHTML;
23759 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23763 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23765 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23771 picker : function()
23773 return this.pickerEl;
23776 fillMonths: function()
23779 var months = this.picker().select('>.datepicker-months td', true).first();
23781 months.dom.innerHTML = '';
23787 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23790 months.createChild(month);
23799 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23800 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23803 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23804 e.removeClass('active');
23806 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23807 e.addClass('active');
23814 if(this.isInline) {
23818 this.picker().removeClass(['bottom', 'top']);
23820 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23822 * place to the top of element!
23826 this.picker().addClass('top');
23827 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23832 this.picker().addClass('bottom');
23834 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23837 onFocus : function()
23839 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23843 onBlur : function()
23845 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23847 var d = this.inputEl().getValue();
23856 this.picker().show();
23857 this.picker().select('>.datepicker-months', true).first().show();
23861 this.fireEvent('show', this, this.date);
23866 if(this.isInline) {
23869 this.picker().hide();
23870 this.fireEvent('hide', this, this.date);
23874 onMousedown: function(e)
23876 e.stopPropagation();
23877 e.preventDefault();
23882 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23886 fireKey: function(e)
23888 if (!this.picker().isVisible()){
23889 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23900 e.preventDefault();
23904 dir = e.keyCode == 37 ? -1 : 1;
23906 this.vIndex = this.vIndex + dir;
23908 if(this.vIndex < 0){
23912 if(this.vIndex > 11){
23916 if(isNaN(this.vIndex)){
23920 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23926 dir = e.keyCode == 38 ? -1 : 1;
23928 this.vIndex = this.vIndex + dir * 4;
23930 if(this.vIndex < 0){
23934 if(this.vIndex > 11){
23938 if(isNaN(this.vIndex)){
23942 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23947 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23948 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23952 e.preventDefault();
23955 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23956 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23972 this.picker().remove();
23977 Roo.apply(Roo.bootstrap.MonthField, {
23996 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23997 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24002 Roo.apply(Roo.bootstrap.MonthField, {
24006 cls: 'datepicker dropdown-menu roo-dynamic',
24010 cls: 'datepicker-months',
24014 cls: 'table-condensed',
24016 Roo.bootstrap.DateField.content
24036 * @class Roo.bootstrap.CheckBox
24037 * @extends Roo.bootstrap.Input
24038 * Bootstrap CheckBox class
24040 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24041 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24042 * @cfg {String} boxLabel The text that appears beside the checkbox
24043 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24044 * @cfg {Boolean} checked initnal the element
24045 * @cfg {Boolean} inline inline the element (default false)
24046 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24047 * @cfg {String} tooltip label tooltip
24050 * Create a new CheckBox
24051 * @param {Object} config The config object
24054 Roo.bootstrap.CheckBox = function(config){
24055 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24060 * Fires when the element is checked or unchecked.
24061 * @param {Roo.bootstrap.CheckBox} this This input
24062 * @param {Boolean} checked The new checked value
24067 * Fires when the element is click.
24068 * @param {Roo.bootstrap.CheckBox} this This input
24075 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24077 inputType: 'checkbox',
24086 // checkbox success does not make any sense really..
24091 getAutoCreate : function()
24093 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24099 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24102 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24108 type : this.inputType,
24109 value : this.inputValue,
24110 cls : 'roo-' + this.inputType, //'form-box',
24111 placeholder : this.placeholder || ''
24115 if(this.inputType != 'radio'){
24119 cls : 'roo-hidden-value',
24120 value : this.checked ? this.inputValue : this.valueOff
24125 if (this.weight) { // Validity check?
24126 cfg.cls += " " + this.inputType + "-" + this.weight;
24129 if (this.disabled) {
24130 input.disabled=true;
24134 input.checked = this.checked;
24139 input.name = this.name;
24141 if(this.inputType != 'radio'){
24142 hidden.name = this.name;
24143 input.name = '_hidden_' + this.name;
24148 input.cls += ' input-' + this.size;
24153 ['xs','sm','md','lg'].map(function(size){
24154 if (settings[size]) {
24155 cfg.cls += ' col-' + size + '-' + settings[size];
24159 var inputblock = input;
24161 if (this.before || this.after) {
24164 cls : 'input-group',
24169 inputblock.cn.push({
24171 cls : 'input-group-addon',
24176 inputblock.cn.push(input);
24178 if(this.inputType != 'radio'){
24179 inputblock.cn.push(hidden);
24183 inputblock.cn.push({
24185 cls : 'input-group-addon',
24191 var boxLabelCfg = false;
24197 //'for': id, // box label is handled by onclick - so no for...
24199 html: this.boxLabel
24202 boxLabelCfg.tooltip = this.tooltip;
24208 if (align ==='left' && this.fieldLabel.length) {
24209 // Roo.log("left and has label");
24214 cls : 'control-label',
24215 html : this.fieldLabel
24226 cfg.cn[1].cn.push(boxLabelCfg);
24229 if(this.labelWidth > 12){
24230 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24233 if(this.labelWidth < 13 && this.labelmd == 0){
24234 this.labelmd = this.labelWidth;
24237 if(this.labellg > 0){
24238 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24239 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24242 if(this.labelmd > 0){
24243 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24244 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24247 if(this.labelsm > 0){
24248 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24249 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24252 if(this.labelxs > 0){
24253 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24254 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24257 } else if ( this.fieldLabel.length) {
24258 // Roo.log(" label");
24262 tag: this.boxLabel ? 'span' : 'label',
24264 cls: 'control-label box-input-label',
24265 //cls : 'input-group-addon',
24266 html : this.fieldLabel
24273 cfg.cn.push(boxLabelCfg);
24278 // Roo.log(" no label && no align");
24279 cfg.cn = [ inputblock ] ;
24281 cfg.cn.push(boxLabelCfg);
24289 if(this.inputType != 'radio'){
24290 cfg.cn.push(hidden);
24298 * return the real input element.
24300 inputEl: function ()
24302 return this.el.select('input.roo-' + this.inputType,true).first();
24304 hiddenEl: function ()
24306 return this.el.select('input.roo-hidden-value',true).first();
24309 labelEl: function()
24311 return this.el.select('label.control-label',true).first();
24313 /* depricated... */
24317 return this.labelEl();
24320 boxLabelEl: function()
24322 return this.el.select('label.box-label',true).first();
24325 initEvents : function()
24327 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24329 this.inputEl().on('click', this.onClick, this);
24331 if (this.boxLabel) {
24332 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24335 this.startValue = this.getValue();
24338 Roo.bootstrap.CheckBox.register(this);
24342 onClick : function(e)
24344 if(this.fireEvent('click', this, e) !== false){
24345 this.setChecked(!this.checked);
24350 setChecked : function(state,suppressEvent)
24352 this.startValue = this.getValue();
24354 if(this.inputType == 'radio'){
24356 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24357 e.dom.checked = false;
24360 this.inputEl().dom.checked = true;
24362 this.inputEl().dom.value = this.inputValue;
24364 if(suppressEvent !== true){
24365 this.fireEvent('check', this, true);
24373 this.checked = state;
24375 this.inputEl().dom.checked = state;
24378 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24380 if(suppressEvent !== true){
24381 this.fireEvent('check', this, state);
24387 getValue : function()
24389 if(this.inputType == 'radio'){
24390 return this.getGroupValue();
24393 return this.hiddenEl().dom.value;
24397 getGroupValue : function()
24399 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24403 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24406 setValue : function(v,suppressEvent)
24408 if(this.inputType == 'radio'){
24409 this.setGroupValue(v, suppressEvent);
24413 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24418 setGroupValue : function(v, suppressEvent)
24420 this.startValue = this.getValue();
24422 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24423 e.dom.checked = false;
24425 if(e.dom.value == v){
24426 e.dom.checked = true;
24430 if(suppressEvent !== true){
24431 this.fireEvent('check', this, true);
24439 validate : function()
24441 if(this.getVisibilityEl().hasClass('hidden')){
24447 (this.inputType == 'radio' && this.validateRadio()) ||
24448 (this.inputType == 'checkbox' && this.validateCheckbox())
24454 this.markInvalid();
24458 validateRadio : function()
24460 if(this.getVisibilityEl().hasClass('hidden')){
24464 if(this.allowBlank){
24470 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24471 if(!e.dom.checked){
24483 validateCheckbox : function()
24486 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24487 //return (this.getValue() == this.inputValue) ? true : false;
24490 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24498 for(var i in group){
24499 if(group[i].el.isVisible(true)){
24507 for(var i in group){
24512 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24519 * Mark this field as valid
24521 markValid : function()
24525 this.fireEvent('valid', this);
24527 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24530 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24537 if(this.inputType == 'radio'){
24538 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24539 var fg = e.findParent('.form-group', false, true);
24540 if (Roo.bootstrap.version == 3) {
24541 fg.removeClass([_this.invalidClass, _this.validClass]);
24542 fg.addClass(_this.validClass);
24544 fg.removeClass(['is-valid', 'is-invalid']);
24545 fg.addClass('is-valid');
24553 var fg = this.el.findParent('.form-group', false, true);
24554 if (Roo.bootstrap.version == 3) {
24555 fg.removeClass([this.invalidClass, this.validClass]);
24556 fg.addClass(this.validClass);
24558 fg.removeClass(['is-valid', 'is-invalid']);
24559 fg.addClass('is-valid');
24564 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24570 for(var i in group){
24571 var fg = group[i].el.findParent('.form-group', false, true);
24572 if (Roo.bootstrap.version == 3) {
24573 fg.removeClass([this.invalidClass, this.validClass]);
24574 fg.addClass(this.validClass);
24576 fg.removeClass(['is-valid', 'is-invalid']);
24577 fg.addClass('is-valid');
24583 * Mark this field as invalid
24584 * @param {String} msg The validation message
24586 markInvalid : function(msg)
24588 if(this.allowBlank){
24594 this.fireEvent('invalid', this, msg);
24596 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24599 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24603 label.markInvalid();
24606 if(this.inputType == 'radio'){
24608 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24609 var fg = e.findParent('.form-group', false, true);
24610 if (Roo.bootstrap.version == 3) {
24611 fg.removeClass([_this.invalidClass, _this.validClass]);
24612 fg.addClass(_this.invalidClass);
24614 fg.removeClass(['is-invalid', 'is-valid']);
24615 fg.addClass('is-invalid');
24623 var fg = this.el.findParent('.form-group', false, true);
24624 if (Roo.bootstrap.version == 3) {
24625 fg.removeClass([_this.invalidClass, _this.validClass]);
24626 fg.addClass(_this.invalidClass);
24628 fg.removeClass(['is-invalid', 'is-valid']);
24629 fg.addClass('is-invalid');
24634 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24640 for(var i in group){
24641 var fg = group[i].el.findParent('.form-group', false, true);
24642 if (Roo.bootstrap.version == 3) {
24643 fg.removeClass([_this.invalidClass, _this.validClass]);
24644 fg.addClass(_this.invalidClass);
24646 fg.removeClass(['is-invalid', 'is-valid']);
24647 fg.addClass('is-invalid');
24653 clearInvalid : function()
24655 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24657 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24659 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24661 if (label && label.iconEl) {
24662 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24663 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24667 disable : function()
24669 if(this.inputType != 'radio'){
24670 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24677 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24678 _this.getActionEl().addClass(this.disabledClass);
24679 e.dom.disabled = true;
24683 this.disabled = true;
24684 this.fireEvent("disable", this);
24688 enable : function()
24690 if(this.inputType != 'radio'){
24691 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24698 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24699 _this.getActionEl().removeClass(this.disabledClass);
24700 e.dom.disabled = false;
24704 this.disabled = false;
24705 this.fireEvent("enable", this);
24709 setBoxLabel : function(v)
24714 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24720 Roo.apply(Roo.bootstrap.CheckBox, {
24725 * register a CheckBox Group
24726 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24728 register : function(checkbox)
24730 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24731 this.groups[checkbox.groupId] = {};
24734 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24738 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24742 * fetch a CheckBox Group based on the group ID
24743 * @param {string} the group ID
24744 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24746 get: function(groupId) {
24747 if (typeof(this.groups[groupId]) == 'undefined') {
24751 return this.groups[groupId] ;
24764 * @class Roo.bootstrap.Radio
24765 * @extends Roo.bootstrap.Component
24766 * Bootstrap Radio class
24767 * @cfg {String} boxLabel - the label associated
24768 * @cfg {String} value - the value of radio
24771 * Create a new Radio
24772 * @param {Object} config The config object
24774 Roo.bootstrap.Radio = function(config){
24775 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24779 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24785 getAutoCreate : function()
24789 cls : 'form-group radio',
24794 html : this.boxLabel
24802 initEvents : function()
24804 this.parent().register(this);
24806 this.el.on('click', this.onClick, this);
24810 onClick : function(e)
24812 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24813 this.setChecked(true);
24817 setChecked : function(state, suppressEvent)
24819 this.parent().setValue(this.value, suppressEvent);
24823 setBoxLabel : function(v)
24828 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24843 * @class Roo.bootstrap.SecurePass
24844 * @extends Roo.bootstrap.Input
24845 * Bootstrap SecurePass class
24849 * Create a new SecurePass
24850 * @param {Object} config The config object
24853 Roo.bootstrap.SecurePass = function (config) {
24854 // these go here, so the translation tool can replace them..
24856 PwdEmpty: "Please type a password, and then retype it to confirm.",
24857 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24858 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24859 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24860 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24861 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24862 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24863 TooWeak: "Your password is Too Weak."
24865 this.meterLabel = "Password strength:";
24866 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24867 this.meterClass = [
24868 "roo-password-meter-tooweak",
24869 "roo-password-meter-weak",
24870 "roo-password-meter-medium",
24871 "roo-password-meter-strong",
24872 "roo-password-meter-grey"
24877 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24880 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24882 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24884 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24885 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24886 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24887 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24888 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24889 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24890 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24900 * @cfg {String/Object} Label for the strength meter (defaults to
24901 * 'Password strength:')
24906 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24907 * ['Weak', 'Medium', 'Strong'])
24910 pwdStrengths: false,
24923 initEvents: function ()
24925 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24927 if (this.el.is('input[type=password]') && Roo.isSafari) {
24928 this.el.on('keydown', this.SafariOnKeyDown, this);
24931 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24934 onRender: function (ct, position)
24936 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24937 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24938 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24940 this.trigger.createChild({
24945 cls: 'roo-password-meter-grey col-xs-12',
24948 //width: this.meterWidth + 'px'
24952 cls: 'roo-password-meter-text'
24958 if (this.hideTrigger) {
24959 this.trigger.setDisplayed(false);
24961 this.setSize(this.width || '', this.height || '');
24964 onDestroy: function ()
24966 if (this.trigger) {
24967 this.trigger.removeAllListeners();
24968 this.trigger.remove();
24971 this.wrap.remove();
24973 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24976 checkStrength: function ()
24978 var pwd = this.inputEl().getValue();
24979 if (pwd == this._lastPwd) {
24984 if (this.ClientSideStrongPassword(pwd)) {
24986 } else if (this.ClientSideMediumPassword(pwd)) {
24988 } else if (this.ClientSideWeakPassword(pwd)) {
24994 Roo.log('strength1: ' + strength);
24996 //var pm = this.trigger.child('div/div/div').dom;
24997 var pm = this.trigger.child('div/div');
24998 pm.removeClass(this.meterClass);
24999 pm.addClass(this.meterClass[strength]);
25002 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25004 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25006 this._lastPwd = pwd;
25010 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25012 this._lastPwd = '';
25014 var pm = this.trigger.child('div/div');
25015 pm.removeClass(this.meterClass);
25016 pm.addClass('roo-password-meter-grey');
25019 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25022 this.inputEl().dom.type='password';
25025 validateValue: function (value)
25027 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25030 if (value.length == 0) {
25031 if (this.allowBlank) {
25032 this.clearInvalid();
25036 this.markInvalid(this.errors.PwdEmpty);
25037 this.errorMsg = this.errors.PwdEmpty;
25045 if (!value.match(/[\x21-\x7e]+/)) {
25046 this.markInvalid(this.errors.PwdBadChar);
25047 this.errorMsg = this.errors.PwdBadChar;
25050 if (value.length < 6) {
25051 this.markInvalid(this.errors.PwdShort);
25052 this.errorMsg = this.errors.PwdShort;
25055 if (value.length > 16) {
25056 this.markInvalid(this.errors.PwdLong);
25057 this.errorMsg = this.errors.PwdLong;
25061 if (this.ClientSideStrongPassword(value)) {
25063 } else if (this.ClientSideMediumPassword(value)) {
25065 } else if (this.ClientSideWeakPassword(value)) {
25072 if (strength < 2) {
25073 //this.markInvalid(this.errors.TooWeak);
25074 this.errorMsg = this.errors.TooWeak;
25079 console.log('strength2: ' + strength);
25081 //var pm = this.trigger.child('div/div/div').dom;
25083 var pm = this.trigger.child('div/div');
25084 pm.removeClass(this.meterClass);
25085 pm.addClass(this.meterClass[strength]);
25087 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25089 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25091 this.errorMsg = '';
25095 CharacterSetChecks: function (type)
25098 this.fResult = false;
25101 isctype: function (character, type)
25104 case this.kCapitalLetter:
25105 if (character >= 'A' && character <= 'Z') {
25110 case this.kSmallLetter:
25111 if (character >= 'a' && character <= 'z') {
25117 if (character >= '0' && character <= '9') {
25122 case this.kPunctuation:
25123 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25134 IsLongEnough: function (pwd, size)
25136 return !(pwd == null || isNaN(size) || pwd.length < size);
25139 SpansEnoughCharacterSets: function (word, nb)
25141 if (!this.IsLongEnough(word, nb))
25146 var characterSetChecks = new Array(
25147 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25148 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25151 for (var index = 0; index < word.length; ++index) {
25152 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25153 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25154 characterSetChecks[nCharSet].fResult = true;
25161 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25162 if (characterSetChecks[nCharSet].fResult) {
25167 if (nCharSets < nb) {
25173 ClientSideStrongPassword: function (pwd)
25175 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25178 ClientSideMediumPassword: function (pwd)
25180 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25183 ClientSideWeakPassword: function (pwd)
25185 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25188 })//<script type="text/javascript">
25191 * Based Ext JS Library 1.1.1
25192 * Copyright(c) 2006-2007, Ext JS, LLC.
25198 * @class Roo.HtmlEditorCore
25199 * @extends Roo.Component
25200 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25202 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25205 Roo.HtmlEditorCore = function(config){
25208 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25213 * @event initialize
25214 * Fires when the editor is fully initialized (including the iframe)
25215 * @param {Roo.HtmlEditorCore} this
25220 * Fires when the editor is first receives the focus. Any insertion must wait
25221 * until after this event.
25222 * @param {Roo.HtmlEditorCore} this
25226 * @event beforesync
25227 * Fires before the textarea is updated with content from the editor iframe. Return false
25228 * to cancel the sync.
25229 * @param {Roo.HtmlEditorCore} this
25230 * @param {String} html
25234 * @event beforepush
25235 * Fires before the iframe editor is updated with content from the textarea. Return false
25236 * to cancel the push.
25237 * @param {Roo.HtmlEditorCore} this
25238 * @param {String} html
25243 * Fires when the textarea is updated with content from the editor iframe.
25244 * @param {Roo.HtmlEditorCore} this
25245 * @param {String} html
25250 * Fires when the iframe editor is updated with content from the textarea.
25251 * @param {Roo.HtmlEditorCore} this
25252 * @param {String} html
25257 * @event editorevent
25258 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25259 * @param {Roo.HtmlEditorCore} this
25265 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25267 // defaults : white / black...
25268 this.applyBlacklists();
25275 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25279 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25285 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25290 * @cfg {Number} height (in pixels)
25294 * @cfg {Number} width (in pixels)
25299 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25302 stylesheets: false,
25307 // private properties
25308 validationEvent : false,
25310 initialized : false,
25312 sourceEditMode : false,
25313 onFocus : Roo.emptyFn,
25315 hideMode:'offsets',
25319 // blacklist + whitelisted elements..
25326 * Protected method that will not generally be called directly. It
25327 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25328 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25330 getDocMarkup : function(){
25334 // inherit styels from page...??
25335 if (this.stylesheets === false) {
25337 Roo.get(document.head).select('style').each(function(node) {
25338 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25341 Roo.get(document.head).select('link').each(function(node) {
25342 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25345 } else if (!this.stylesheets.length) {
25347 st = '<style type="text/css">' +
25348 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25351 for (var i in this.stylesheets) {
25352 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25357 st += '<style type="text/css">' +
25358 'IMG { cursor: pointer } ' +
25361 var cls = 'roo-htmleditor-body';
25363 if(this.bodyCls.length){
25364 cls += ' ' + this.bodyCls;
25367 return '<html><head>' + st +
25368 //<style type="text/css">' +
25369 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25371 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25375 onRender : function(ct, position)
25378 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25379 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25382 this.el.dom.style.border = '0 none';
25383 this.el.dom.setAttribute('tabIndex', -1);
25384 this.el.addClass('x-hidden hide');
25388 if(Roo.isIE){ // fix IE 1px bogus margin
25389 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25393 this.frameId = Roo.id();
25397 var iframe = this.owner.wrap.createChild({
25399 cls: 'form-control', // bootstrap..
25401 name: this.frameId,
25402 frameBorder : 'no',
25403 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25408 this.iframe = iframe.dom;
25410 this.assignDocWin();
25412 this.doc.designMode = 'on';
25415 this.doc.write(this.getDocMarkup());
25419 var task = { // must defer to wait for browser to be ready
25421 //console.log("run task?" + this.doc.readyState);
25422 this.assignDocWin();
25423 if(this.doc.body || this.doc.readyState == 'complete'){
25425 this.doc.designMode="on";
25429 Roo.TaskMgr.stop(task);
25430 this.initEditor.defer(10, this);
25437 Roo.TaskMgr.start(task);
25442 onResize : function(w, h)
25444 Roo.log('resize: ' +w + ',' + h );
25445 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25449 if(typeof w == 'number'){
25451 this.iframe.style.width = w + 'px';
25453 if(typeof h == 'number'){
25455 this.iframe.style.height = h + 'px';
25457 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25464 * Toggles the editor between standard and source edit mode.
25465 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25467 toggleSourceEdit : function(sourceEditMode){
25469 this.sourceEditMode = sourceEditMode === true;
25471 if(this.sourceEditMode){
25473 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25476 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25477 //this.iframe.className = '';
25480 //this.setSize(this.owner.wrap.getSize());
25481 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25488 * Protected method that will not generally be called directly. If you need/want
25489 * custom HTML cleanup, this is the method you should override.
25490 * @param {String} html The HTML to be cleaned
25491 * return {String} The cleaned HTML
25493 cleanHtml : function(html){
25494 html = String(html);
25495 if(html.length > 5){
25496 if(Roo.isSafari){ // strip safari nonsense
25497 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25500 if(html == ' '){
25507 * HTML Editor -> Textarea
25508 * Protected method that will not generally be called directly. Syncs the contents
25509 * of the editor iframe with the textarea.
25511 syncValue : function(){
25512 if(this.initialized){
25513 var bd = (this.doc.body || this.doc.documentElement);
25514 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25515 var html = bd.innerHTML;
25517 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25518 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25520 html = '<div style="'+m[0]+'">' + html + '</div>';
25523 html = this.cleanHtml(html);
25524 // fix up the special chars.. normaly like back quotes in word...
25525 // however we do not want to do this with chinese..
25526 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25528 var cc = match.charCodeAt();
25530 // Get the character value, handling surrogate pairs
25531 if (match.length == 2) {
25532 // It's a surrogate pair, calculate the Unicode code point
25533 var high = match.charCodeAt(0) - 0xD800;
25534 var low = match.charCodeAt(1) - 0xDC00;
25535 cc = (high * 0x400) + low + 0x10000;
25537 (cc >= 0x4E00 && cc < 0xA000 ) ||
25538 (cc >= 0x3400 && cc < 0x4E00 ) ||
25539 (cc >= 0xf900 && cc < 0xfb00 )
25544 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25545 return "&#" + cc + ";";
25552 if(this.owner.fireEvent('beforesync', this, html) !== false){
25553 this.el.dom.value = html;
25554 this.owner.fireEvent('sync', this, html);
25560 * Protected method that will not generally be called directly. Pushes the value of the textarea
25561 * into the iframe editor.
25563 pushValue : function(){
25564 if(this.initialized){
25565 var v = this.el.dom.value.trim();
25567 // if(v.length < 1){
25571 if(this.owner.fireEvent('beforepush', this, v) !== false){
25572 var d = (this.doc.body || this.doc.documentElement);
25574 this.cleanUpPaste();
25575 this.el.dom.value = d.innerHTML;
25576 this.owner.fireEvent('push', this, v);
25582 deferFocus : function(){
25583 this.focus.defer(10, this);
25587 focus : function(){
25588 if(this.win && !this.sourceEditMode){
25595 assignDocWin: function()
25597 var iframe = this.iframe;
25600 this.doc = iframe.contentWindow.document;
25601 this.win = iframe.contentWindow;
25603 // if (!Roo.get(this.frameId)) {
25606 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25607 // this.win = Roo.get(this.frameId).dom.contentWindow;
25609 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25613 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25614 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25619 initEditor : function(){
25620 //console.log("INIT EDITOR");
25621 this.assignDocWin();
25625 this.doc.designMode="on";
25627 this.doc.write(this.getDocMarkup());
25630 var dbody = (this.doc.body || this.doc.documentElement);
25631 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25632 // this copies styles from the containing element into thsi one..
25633 // not sure why we need all of this..
25634 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25636 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25637 //ss['background-attachment'] = 'fixed'; // w3c
25638 dbody.bgProperties = 'fixed'; // ie
25639 //Roo.DomHelper.applyStyles(dbody, ss);
25640 Roo.EventManager.on(this.doc, {
25641 //'mousedown': this.onEditorEvent,
25642 'mouseup': this.onEditorEvent,
25643 'dblclick': this.onEditorEvent,
25644 'click': this.onEditorEvent,
25645 'keyup': this.onEditorEvent,
25650 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25652 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25653 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25655 this.initialized = true;
25657 this.owner.fireEvent('initialize', this);
25662 onDestroy : function(){
25668 //for (var i =0; i < this.toolbars.length;i++) {
25669 // // fixme - ask toolbars for heights?
25670 // this.toolbars[i].onDestroy();
25673 //this.wrap.dom.innerHTML = '';
25674 //this.wrap.remove();
25679 onFirstFocus : function(){
25681 this.assignDocWin();
25684 this.activated = true;
25687 if(Roo.isGecko){ // prevent silly gecko errors
25689 var s = this.win.getSelection();
25690 if(!s.focusNode || s.focusNode.nodeType != 3){
25691 var r = s.getRangeAt(0);
25692 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25697 this.execCmd('useCSS', true);
25698 this.execCmd('styleWithCSS', false);
25701 this.owner.fireEvent('activate', this);
25705 adjustFont: function(btn){
25706 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25707 //if(Roo.isSafari){ // safari
25710 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25711 if(Roo.isSafari){ // safari
25712 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25713 v = (v < 10) ? 10 : v;
25714 v = (v > 48) ? 48 : v;
25715 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25720 v = Math.max(1, v+adjust);
25722 this.execCmd('FontSize', v );
25725 onEditorEvent : function(e)
25727 this.owner.fireEvent('editorevent', this, e);
25728 // this.updateToolbar();
25729 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25732 insertTag : function(tg)
25734 // could be a bit smarter... -> wrap the current selected tRoo..
25735 if (tg.toLowerCase() == 'span' ||
25736 tg.toLowerCase() == 'code' ||
25737 tg.toLowerCase() == 'sup' ||
25738 tg.toLowerCase() == 'sub'
25741 range = this.createRange(this.getSelection());
25742 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25743 wrappingNode.appendChild(range.extractContents());
25744 range.insertNode(wrappingNode);
25751 this.execCmd("formatblock", tg);
25755 insertText : function(txt)
25759 var range = this.createRange();
25760 range.deleteContents();
25761 //alert(Sender.getAttribute('label'));
25763 range.insertNode(this.doc.createTextNode(txt));
25769 * Executes a Midas editor command on the editor document and performs necessary focus and
25770 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25771 * @param {String} cmd The Midas command
25772 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25774 relayCmd : function(cmd, value){
25776 this.execCmd(cmd, value);
25777 this.owner.fireEvent('editorevent', this);
25778 //this.updateToolbar();
25779 this.owner.deferFocus();
25783 * Executes a Midas editor command directly on the editor document.
25784 * For visual commands, you should use {@link #relayCmd} instead.
25785 * <b>This should only be called after the editor is initialized.</b>
25786 * @param {String} cmd The Midas command
25787 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25789 execCmd : function(cmd, value){
25790 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25797 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25799 * @param {String} text | dom node..
25801 insertAtCursor : function(text)
25804 if(!this.activated){
25810 var r = this.doc.selection.createRange();
25821 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25825 // from jquery ui (MIT licenced)
25827 var win = this.win;
25829 if (win.getSelection && win.getSelection().getRangeAt) {
25830 range = win.getSelection().getRangeAt(0);
25831 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25832 range.insertNode(node);
25833 } else if (win.document.selection && win.document.selection.createRange) {
25834 // no firefox support
25835 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25836 win.document.selection.createRange().pasteHTML(txt);
25838 // no firefox support
25839 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25840 this.execCmd('InsertHTML', txt);
25849 mozKeyPress : function(e){
25851 var c = e.getCharCode(), cmd;
25854 c = String.fromCharCode(c).toLowerCase();
25868 this.cleanUpPaste.defer(100, this);
25876 e.preventDefault();
25884 fixKeys : function(){ // load time branching for fastest keydown performance
25886 return function(e){
25887 var k = e.getKey(), r;
25890 r = this.doc.selection.createRange();
25893 r.pasteHTML('    ');
25900 r = this.doc.selection.createRange();
25902 var target = r.parentElement();
25903 if(!target || target.tagName.toLowerCase() != 'li'){
25905 r.pasteHTML('<br />');
25911 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25912 this.cleanUpPaste.defer(100, this);
25918 }else if(Roo.isOpera){
25919 return function(e){
25920 var k = e.getKey();
25924 this.execCmd('InsertHTML','    ');
25927 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25928 this.cleanUpPaste.defer(100, this);
25933 }else if(Roo.isSafari){
25934 return function(e){
25935 var k = e.getKey();
25939 this.execCmd('InsertText','\t');
25943 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25944 this.cleanUpPaste.defer(100, this);
25952 getAllAncestors: function()
25954 var p = this.getSelectedNode();
25957 a.push(p); // push blank onto stack..
25958 p = this.getParentElement();
25962 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25966 a.push(this.doc.body);
25970 lastSelNode : false,
25973 getSelection : function()
25975 this.assignDocWin();
25976 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25979 getSelectedNode: function()
25981 // this may only work on Gecko!!!
25983 // should we cache this!!!!
25988 var range = this.createRange(this.getSelection()).cloneRange();
25991 var parent = range.parentElement();
25993 var testRange = range.duplicate();
25994 testRange.moveToElementText(parent);
25995 if (testRange.inRange(range)) {
25998 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26001 parent = parent.parentElement;
26006 // is ancestor a text element.
26007 var ac = range.commonAncestorContainer;
26008 if (ac.nodeType == 3) {
26009 ac = ac.parentNode;
26012 var ar = ac.childNodes;
26015 var other_nodes = [];
26016 var has_other_nodes = false;
26017 for (var i=0;i<ar.length;i++) {
26018 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26021 // fullly contained node.
26023 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26028 // probably selected..
26029 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26030 other_nodes.push(ar[i]);
26034 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26039 has_other_nodes = true;
26041 if (!nodes.length && other_nodes.length) {
26042 nodes= other_nodes;
26044 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26050 createRange: function(sel)
26052 // this has strange effects when using with
26053 // top toolbar - not sure if it's a great idea.
26054 //this.editor.contentWindow.focus();
26055 if (typeof sel != "undefined") {
26057 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26059 return this.doc.createRange();
26062 return this.doc.createRange();
26065 getParentElement: function()
26068 this.assignDocWin();
26069 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26071 var range = this.createRange(sel);
26074 var p = range.commonAncestorContainer;
26075 while (p.nodeType == 3) { // text node
26086 * Range intersection.. the hard stuff...
26090 * [ -- selected range --- ]
26094 * if end is before start or hits it. fail.
26095 * if start is after end or hits it fail.
26097 * if either hits (but other is outside. - then it's not
26103 // @see http://www.thismuchiknow.co.uk/?p=64.
26104 rangeIntersectsNode : function(range, node)
26106 var nodeRange = node.ownerDocument.createRange();
26108 nodeRange.selectNode(node);
26110 nodeRange.selectNodeContents(node);
26113 var rangeStartRange = range.cloneRange();
26114 rangeStartRange.collapse(true);
26116 var rangeEndRange = range.cloneRange();
26117 rangeEndRange.collapse(false);
26119 var nodeStartRange = nodeRange.cloneRange();
26120 nodeStartRange.collapse(true);
26122 var nodeEndRange = nodeRange.cloneRange();
26123 nodeEndRange.collapse(false);
26125 return rangeStartRange.compareBoundaryPoints(
26126 Range.START_TO_START, nodeEndRange) == -1 &&
26127 rangeEndRange.compareBoundaryPoints(
26128 Range.START_TO_START, nodeStartRange) == 1;
26132 rangeCompareNode : function(range, node)
26134 var nodeRange = node.ownerDocument.createRange();
26136 nodeRange.selectNode(node);
26138 nodeRange.selectNodeContents(node);
26142 range.collapse(true);
26144 nodeRange.collapse(true);
26146 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26147 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26149 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26151 var nodeIsBefore = ss == 1;
26152 var nodeIsAfter = ee == -1;
26154 if (nodeIsBefore && nodeIsAfter) {
26157 if (!nodeIsBefore && nodeIsAfter) {
26158 return 1; //right trailed.
26161 if (nodeIsBefore && !nodeIsAfter) {
26162 return 2; // left trailed.
26168 // private? - in a new class?
26169 cleanUpPaste : function()
26171 // cleans up the whole document..
26172 Roo.log('cleanuppaste');
26174 this.cleanUpChildren(this.doc.body);
26175 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26176 if (clean != this.doc.body.innerHTML) {
26177 this.doc.body.innerHTML = clean;
26182 cleanWordChars : function(input) {// change the chars to hex code
26183 var he = Roo.HtmlEditorCore;
26185 var output = input;
26186 Roo.each(he.swapCodes, function(sw) {
26187 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26189 output = output.replace(swapper, sw[1]);
26196 cleanUpChildren : function (n)
26198 if (!n.childNodes.length) {
26201 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26202 this.cleanUpChild(n.childNodes[i]);
26209 cleanUpChild : function (node)
26212 //console.log(node);
26213 if (node.nodeName == "#text") {
26214 // clean up silly Windows -- stuff?
26217 if (node.nodeName == "#comment") {
26218 node.parentNode.removeChild(node);
26219 // clean up silly Windows -- stuff?
26222 var lcname = node.tagName.toLowerCase();
26223 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26224 // whitelist of tags..
26226 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26228 node.parentNode.removeChild(node);
26233 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26235 // spans with no attributes - just remove them..
26236 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26237 remove_keep_children = true;
26240 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26241 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26243 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26244 // remove_keep_children = true;
26247 if (remove_keep_children) {
26248 this.cleanUpChildren(node);
26249 // inserts everything just before this node...
26250 while (node.childNodes.length) {
26251 var cn = node.childNodes[0];
26252 node.removeChild(cn);
26253 node.parentNode.insertBefore(cn, node);
26255 node.parentNode.removeChild(node);
26259 if (!node.attributes || !node.attributes.length) {
26264 this.cleanUpChildren(node);
26268 function cleanAttr(n,v)
26271 if (v.match(/^\./) || v.match(/^\//)) {
26274 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26277 if (v.match(/^#/)) {
26280 if (v.match(/^\{/)) { // allow template editing.
26283 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26284 node.removeAttribute(n);
26288 var cwhite = this.cwhite;
26289 var cblack = this.cblack;
26291 function cleanStyle(n,v)
26293 if (v.match(/expression/)) { //XSS?? should we even bother..
26294 node.removeAttribute(n);
26298 var parts = v.split(/;/);
26301 Roo.each(parts, function(p) {
26302 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26306 var l = p.split(':').shift().replace(/\s+/g,'');
26307 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26309 if ( cwhite.length && cblack.indexOf(l) > -1) {
26310 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26311 //node.removeAttribute(n);
26315 // only allow 'c whitelisted system attributes'
26316 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26317 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26318 //node.removeAttribute(n);
26328 if (clean.length) {
26329 node.setAttribute(n, clean.join(';'));
26331 node.removeAttribute(n);
26337 for (var i = node.attributes.length-1; i > -1 ; i--) {
26338 var a = node.attributes[i];
26341 if (a.name.toLowerCase().substr(0,2)=='on') {
26342 node.removeAttribute(a.name);
26345 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26346 node.removeAttribute(a.name);
26349 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26350 cleanAttr(a.name,a.value); // fixme..
26353 if (a.name == 'style') {
26354 cleanStyle(a.name,a.value);
26357 /// clean up MS crap..
26358 // tecnically this should be a list of valid class'es..
26361 if (a.name == 'class') {
26362 if (a.value.match(/^Mso/)) {
26363 node.removeAttribute('class');
26366 if (a.value.match(/^body$/)) {
26367 node.removeAttribute('class');
26378 this.cleanUpChildren(node);
26384 * Clean up MS wordisms...
26386 cleanWord : function(node)
26389 this.cleanWord(this.doc.body);
26394 node.nodeName == 'SPAN' &&
26395 !node.hasAttributes() &&
26396 node.childNodes.length == 1 &&
26397 node.firstChild.nodeName == "#text"
26399 var textNode = node.firstChild;
26400 node.removeChild(textNode);
26401 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26402 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26404 node.parentNode.insertBefore(textNode, node);
26405 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26406 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26408 node.parentNode.removeChild(node);
26411 if (node.nodeName == "#text") {
26412 // clean up silly Windows -- stuff?
26415 if (node.nodeName == "#comment") {
26416 node.parentNode.removeChild(node);
26417 // clean up silly Windows -- stuff?
26421 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26422 node.parentNode.removeChild(node);
26425 //Roo.log(node.tagName);
26426 // remove - but keep children..
26427 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26428 //Roo.log('-- removed');
26429 while (node.childNodes.length) {
26430 var cn = node.childNodes[0];
26431 node.removeChild(cn);
26432 node.parentNode.insertBefore(cn, node);
26433 // move node to parent - and clean it..
26434 this.cleanWord(cn);
26436 node.parentNode.removeChild(node);
26437 /// no need to iterate chidlren = it's got none..
26438 //this.iterateChildren(node, this.cleanWord);
26442 if (node.className.length) {
26444 var cn = node.className.split(/\W+/);
26446 Roo.each(cn, function(cls) {
26447 if (cls.match(/Mso[a-zA-Z]+/)) {
26452 node.className = cna.length ? cna.join(' ') : '';
26454 node.removeAttribute("class");
26458 if (node.hasAttribute("lang")) {
26459 node.removeAttribute("lang");
26462 if (node.hasAttribute("style")) {
26464 var styles = node.getAttribute("style").split(";");
26466 Roo.each(styles, function(s) {
26467 if (!s.match(/:/)) {
26470 var kv = s.split(":");
26471 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26474 // what ever is left... we allow.
26477 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26478 if (!nstyle.length) {
26479 node.removeAttribute('style');
26482 this.iterateChildren(node, this.cleanWord);
26488 * iterateChildren of a Node, calling fn each time, using this as the scole..
26489 * @param {DomNode} node node to iterate children of.
26490 * @param {Function} fn method of this class to call on each item.
26492 iterateChildren : function(node, fn)
26494 if (!node.childNodes.length) {
26497 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26498 fn.call(this, node.childNodes[i])
26504 * cleanTableWidths.
26506 * Quite often pasting from word etc.. results in tables with column and widths.
26507 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26510 cleanTableWidths : function(node)
26515 this.cleanTableWidths(this.doc.body);
26520 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26523 Roo.log(node.tagName);
26524 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26525 this.iterateChildren(node, this.cleanTableWidths);
26528 if (node.hasAttribute('width')) {
26529 node.removeAttribute('width');
26533 if (node.hasAttribute("style")) {
26536 var styles = node.getAttribute("style").split(";");
26538 Roo.each(styles, function(s) {
26539 if (!s.match(/:/)) {
26542 var kv = s.split(":");
26543 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26546 // what ever is left... we allow.
26549 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26550 if (!nstyle.length) {
26551 node.removeAttribute('style');
26555 this.iterateChildren(node, this.cleanTableWidths);
26563 domToHTML : function(currentElement, depth, nopadtext) {
26565 depth = depth || 0;
26566 nopadtext = nopadtext || false;
26568 if (!currentElement) {
26569 return this.domToHTML(this.doc.body);
26572 //Roo.log(currentElement);
26574 var allText = false;
26575 var nodeName = currentElement.nodeName;
26576 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26578 if (nodeName == '#text') {
26580 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26585 if (nodeName != 'BODY') {
26588 // Prints the node tagName, such as <A>, <IMG>, etc
26591 for(i = 0; i < currentElement.attributes.length;i++) {
26593 var aname = currentElement.attributes.item(i).name;
26594 if (!currentElement.attributes.item(i).value.length) {
26597 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26600 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26609 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26612 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26617 // Traverse the tree
26619 var currentElementChild = currentElement.childNodes.item(i);
26620 var allText = true;
26621 var innerHTML = '';
26623 while (currentElementChild) {
26624 // Formatting code (indent the tree so it looks nice on the screen)
26625 var nopad = nopadtext;
26626 if (lastnode == 'SPAN') {
26630 if (currentElementChild.nodeName == '#text') {
26631 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26632 toadd = nopadtext ? toadd : toadd.trim();
26633 if (!nopad && toadd.length > 80) {
26634 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26636 innerHTML += toadd;
26639 currentElementChild = currentElement.childNodes.item(i);
26645 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26647 // Recursively traverse the tree structure of the child node
26648 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26649 lastnode = currentElementChild.nodeName;
26651 currentElementChild=currentElement.childNodes.item(i);
26657 // The remaining code is mostly for formatting the tree
26658 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26663 ret+= "</"+tagName+">";
26669 applyBlacklists : function()
26671 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26672 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26676 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26677 if (b.indexOf(tag) > -1) {
26680 this.white.push(tag);
26684 Roo.each(w, function(tag) {
26685 if (b.indexOf(tag) > -1) {
26688 if (this.white.indexOf(tag) > -1) {
26691 this.white.push(tag);
26696 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26697 if (w.indexOf(tag) > -1) {
26700 this.black.push(tag);
26704 Roo.each(b, function(tag) {
26705 if (w.indexOf(tag) > -1) {
26708 if (this.black.indexOf(tag) > -1) {
26711 this.black.push(tag);
26716 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26717 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26721 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26722 if (b.indexOf(tag) > -1) {
26725 this.cwhite.push(tag);
26729 Roo.each(w, function(tag) {
26730 if (b.indexOf(tag) > -1) {
26733 if (this.cwhite.indexOf(tag) > -1) {
26736 this.cwhite.push(tag);
26741 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26742 if (w.indexOf(tag) > -1) {
26745 this.cblack.push(tag);
26749 Roo.each(b, function(tag) {
26750 if (w.indexOf(tag) > -1) {
26753 if (this.cblack.indexOf(tag) > -1) {
26756 this.cblack.push(tag);
26761 setStylesheets : function(stylesheets)
26763 if(typeof(stylesheets) == 'string'){
26764 Roo.get(this.iframe.contentDocument.head).createChild({
26766 rel : 'stylesheet',
26775 Roo.each(stylesheets, function(s) {
26780 Roo.get(_this.iframe.contentDocument.head).createChild({
26782 rel : 'stylesheet',
26791 removeStylesheets : function()
26795 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26800 setStyle : function(style)
26802 Roo.get(this.iframe.contentDocument.head).createChild({
26811 // hide stuff that is not compatible
26825 * @event specialkey
26829 * @cfg {String} fieldClass @hide
26832 * @cfg {String} focusClass @hide
26835 * @cfg {String} autoCreate @hide
26838 * @cfg {String} inputType @hide
26841 * @cfg {String} invalidClass @hide
26844 * @cfg {String} invalidText @hide
26847 * @cfg {String} msgFx @hide
26850 * @cfg {String} validateOnBlur @hide
26854 Roo.HtmlEditorCore.white = [
26855 'area', 'br', 'img', 'input', 'hr', 'wbr',
26857 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26858 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26859 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26860 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26861 'table', 'ul', 'xmp',
26863 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26866 'dir', 'menu', 'ol', 'ul', 'dl',
26872 Roo.HtmlEditorCore.black = [
26873 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26875 'base', 'basefont', 'bgsound', 'blink', 'body',
26876 'frame', 'frameset', 'head', 'html', 'ilayer',
26877 'iframe', 'layer', 'link', 'meta', 'object',
26878 'script', 'style' ,'title', 'xml' // clean later..
26880 Roo.HtmlEditorCore.clean = [
26881 'script', 'style', 'title', 'xml'
26883 Roo.HtmlEditorCore.remove = [
26888 Roo.HtmlEditorCore.ablack = [
26892 Roo.HtmlEditorCore.aclean = [
26893 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26897 Roo.HtmlEditorCore.pwhite= [
26898 'http', 'https', 'mailto'
26901 // white listed style attributes.
26902 Roo.HtmlEditorCore.cwhite= [
26903 // 'text-align', /// default is to allow most things..
26909 // black listed style attributes.
26910 Roo.HtmlEditorCore.cblack= [
26911 // 'font-size' -- this can be set by the project
26915 Roo.HtmlEditorCore.swapCodes =[
26916 [ 8211, "–" ],
26917 [ 8212, "—" ],
26934 * @class Roo.bootstrap.HtmlEditor
26935 * @extends Roo.bootstrap.TextArea
26936 * Bootstrap HtmlEditor class
26939 * Create a new HtmlEditor
26940 * @param {Object} config The config object
26943 Roo.bootstrap.HtmlEditor = function(config){
26944 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26945 if (!this.toolbars) {
26946 this.toolbars = [];
26949 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26952 * @event initialize
26953 * Fires when the editor is fully initialized (including the iframe)
26954 * @param {HtmlEditor} this
26959 * Fires when the editor is first receives the focus. Any insertion must wait
26960 * until after this event.
26961 * @param {HtmlEditor} this
26965 * @event beforesync
26966 * Fires before the textarea is updated with content from the editor iframe. Return false
26967 * to cancel the sync.
26968 * @param {HtmlEditor} this
26969 * @param {String} html
26973 * @event beforepush
26974 * Fires before the iframe editor is updated with content from the textarea. Return false
26975 * to cancel the push.
26976 * @param {HtmlEditor} this
26977 * @param {String} html
26982 * Fires when the textarea is updated with content from the editor iframe.
26983 * @param {HtmlEditor} this
26984 * @param {String} html
26989 * Fires when the iframe editor is updated with content from the textarea.
26990 * @param {HtmlEditor} this
26991 * @param {String} html
26995 * @event editmodechange
26996 * Fires when the editor switches edit modes
26997 * @param {HtmlEditor} this
26998 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27000 editmodechange: true,
27002 * @event editorevent
27003 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27004 * @param {HtmlEditor} this
27008 * @event firstfocus
27009 * Fires when on first focus - needed by toolbars..
27010 * @param {HtmlEditor} this
27015 * Auto save the htmlEditor value as a file into Events
27016 * @param {HtmlEditor} this
27020 * @event savedpreview
27021 * preview the saved version of htmlEditor
27022 * @param {HtmlEditor} this
27029 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27033 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27038 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27043 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27048 * @cfg {Number} height (in pixels)
27052 * @cfg {Number} width (in pixels)
27057 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27060 stylesheets: false,
27065 // private properties
27066 validationEvent : false,
27068 initialized : false,
27071 onFocus : Roo.emptyFn,
27073 hideMode:'offsets',
27075 tbContainer : false,
27079 toolbarContainer :function() {
27080 return this.wrap.select('.x-html-editor-tb',true).first();
27084 * Protected method that will not generally be called directly. It
27085 * is called when the editor creates its toolbar. Override this method if you need to
27086 * add custom toolbar buttons.
27087 * @param {HtmlEditor} editor
27089 createToolbar : function(){
27090 Roo.log('renewing');
27091 Roo.log("create toolbars");
27093 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27094 this.toolbars[0].render(this.toolbarContainer());
27098 // if (!editor.toolbars || !editor.toolbars.length) {
27099 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27102 // for (var i =0 ; i < editor.toolbars.length;i++) {
27103 // editor.toolbars[i] = Roo.factory(
27104 // typeof(editor.toolbars[i]) == 'string' ?
27105 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27106 // Roo.bootstrap.HtmlEditor);
27107 // editor.toolbars[i].init(editor);
27113 onRender : function(ct, position)
27115 // Roo.log("Call onRender: " + this.xtype);
27117 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27119 this.wrap = this.inputEl().wrap({
27120 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27123 this.editorcore.onRender(ct, position);
27125 if (this.resizable) {
27126 this.resizeEl = new Roo.Resizable(this.wrap, {
27130 minHeight : this.height,
27131 height: this.height,
27132 handles : this.resizable,
27135 resize : function(r, w, h) {
27136 _t.onResize(w,h); // -something
27142 this.createToolbar(this);
27145 if(!this.width && this.resizable){
27146 this.setSize(this.wrap.getSize());
27148 if (this.resizeEl) {
27149 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27150 // should trigger onReize..
27156 onResize : function(w, h)
27158 Roo.log('resize: ' +w + ',' + h );
27159 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27163 if(this.inputEl() ){
27164 if(typeof w == 'number'){
27165 var aw = w - this.wrap.getFrameWidth('lr');
27166 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27169 if(typeof h == 'number'){
27170 var tbh = -11; // fixme it needs to tool bar size!
27171 for (var i =0; i < this.toolbars.length;i++) {
27172 // fixme - ask toolbars for heights?
27173 tbh += this.toolbars[i].el.getHeight();
27174 //if (this.toolbars[i].footer) {
27175 // tbh += this.toolbars[i].footer.el.getHeight();
27183 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27184 ah -= 5; // knock a few pixes off for look..
27185 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27189 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27190 this.editorcore.onResize(ew,eh);
27195 * Toggles the editor between standard and source edit mode.
27196 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27198 toggleSourceEdit : function(sourceEditMode)
27200 this.editorcore.toggleSourceEdit(sourceEditMode);
27202 if(this.editorcore.sourceEditMode){
27203 Roo.log('editor - showing textarea');
27206 // Roo.log(this.syncValue());
27208 this.inputEl().removeClass(['hide', 'x-hidden']);
27209 this.inputEl().dom.removeAttribute('tabIndex');
27210 this.inputEl().focus();
27212 Roo.log('editor - hiding textarea');
27214 // Roo.log(this.pushValue());
27217 this.inputEl().addClass(['hide', 'x-hidden']);
27218 this.inputEl().dom.setAttribute('tabIndex', -1);
27219 //this.deferFocus();
27222 if(this.resizable){
27223 this.setSize(this.wrap.getSize());
27226 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27229 // private (for BoxComponent)
27230 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27232 // private (for BoxComponent)
27233 getResizeEl : function(){
27237 // private (for BoxComponent)
27238 getPositionEl : function(){
27243 initEvents : function(){
27244 this.originalValue = this.getValue();
27248 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27251 // markInvalid : Roo.emptyFn,
27253 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27256 // clearInvalid : Roo.emptyFn,
27258 setValue : function(v){
27259 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27260 this.editorcore.pushValue();
27265 deferFocus : function(){
27266 this.focus.defer(10, this);
27270 focus : function(){
27271 this.editorcore.focus();
27277 onDestroy : function(){
27283 for (var i =0; i < this.toolbars.length;i++) {
27284 // fixme - ask toolbars for heights?
27285 this.toolbars[i].onDestroy();
27288 this.wrap.dom.innerHTML = '';
27289 this.wrap.remove();
27294 onFirstFocus : function(){
27295 //Roo.log("onFirstFocus");
27296 this.editorcore.onFirstFocus();
27297 for (var i =0; i < this.toolbars.length;i++) {
27298 this.toolbars[i].onFirstFocus();
27304 syncValue : function()
27306 this.editorcore.syncValue();
27309 pushValue : function()
27311 this.editorcore.pushValue();
27315 // hide stuff that is not compatible
27329 * @event specialkey
27333 * @cfg {String} fieldClass @hide
27336 * @cfg {String} focusClass @hide
27339 * @cfg {String} autoCreate @hide
27342 * @cfg {String} inputType @hide
27346 * @cfg {String} invalidText @hide
27349 * @cfg {String} msgFx @hide
27352 * @cfg {String} validateOnBlur @hide
27361 Roo.namespace('Roo.bootstrap.htmleditor');
27363 * @class Roo.bootstrap.HtmlEditorToolbar1
27369 new Roo.bootstrap.HtmlEditor({
27372 new Roo.bootstrap.HtmlEditorToolbar1({
27373 disable : { fonts: 1 , format: 1, ..., ... , ...],
27379 * @cfg {Object} disable List of elements to disable..
27380 * @cfg {Array} btns List of additional buttons.
27384 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27387 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27390 Roo.apply(this, config);
27392 // default disabled, based on 'good practice'..
27393 this.disable = this.disable || {};
27394 Roo.applyIf(this.disable, {
27397 specialElements : true
27399 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27401 this.editor = config.editor;
27402 this.editorcore = config.editor.editorcore;
27404 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27406 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27407 // dont call parent... till later.
27409 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27414 editorcore : false,
27419 "h1","h2","h3","h4","h5","h6",
27421 "abbr", "acronym", "address", "cite", "samp", "var",
27425 onRender : function(ct, position)
27427 // Roo.log("Call onRender: " + this.xtype);
27429 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27431 this.el.dom.style.marginBottom = '0';
27433 var editorcore = this.editorcore;
27434 var editor= this.editor;
27437 var btn = function(id,cmd , toggle, handler, html){
27439 var event = toggle ? 'toggle' : 'click';
27444 xns: Roo.bootstrap,
27448 enableToggle:toggle !== false,
27450 pressed : toggle ? false : null,
27453 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27454 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27460 // var cb_box = function...
27465 xns: Roo.bootstrap,
27470 xns: Roo.bootstrap,
27474 Roo.each(this.formats, function(f) {
27475 style.menu.items.push({
27477 xns: Roo.bootstrap,
27478 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27483 editorcore.insertTag(this.tagname);
27490 children.push(style);
27492 btn('bold',false,true);
27493 btn('italic',false,true);
27494 btn('align-left', 'justifyleft',true);
27495 btn('align-center', 'justifycenter',true);
27496 btn('align-right' , 'justifyright',true);
27497 btn('link', false, false, function(btn) {
27498 //Roo.log("create link?");
27499 var url = prompt(this.createLinkText, this.defaultLinkValue);
27500 if(url && url != 'http:/'+'/'){
27501 this.editorcore.relayCmd('createlink', url);
27504 btn('list','insertunorderedlist',true);
27505 btn('pencil', false,true, function(btn){
27507 this.toggleSourceEdit(btn.pressed);
27510 if (this.editor.btns.length > 0) {
27511 for (var i = 0; i<this.editor.btns.length; i++) {
27512 children.push(this.editor.btns[i]);
27520 xns: Roo.bootstrap,
27525 xns: Roo.bootstrap,
27530 cog.menu.items.push({
27532 xns: Roo.bootstrap,
27533 html : Clean styles,
27538 editorcore.insertTag(this.tagname);
27547 this.xtype = 'NavSimplebar';
27549 for(var i=0;i< children.length;i++) {
27551 this.buttons.add(this.addxtypeChild(children[i]));
27555 editor.on('editorevent', this.updateToolbar, this);
27557 onBtnClick : function(id)
27559 this.editorcore.relayCmd(id);
27560 this.editorcore.focus();
27564 * Protected method that will not generally be called directly. It triggers
27565 * a toolbar update by reading the markup state of the current selection in the editor.
27567 updateToolbar: function(){
27569 if(!this.editorcore.activated){
27570 this.editor.onFirstFocus(); // is this neeed?
27574 var btns = this.buttons;
27575 var doc = this.editorcore.doc;
27576 btns.get('bold').setActive(doc.queryCommandState('bold'));
27577 btns.get('italic').setActive(doc.queryCommandState('italic'));
27578 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27580 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27581 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27582 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27584 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27585 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27588 var ans = this.editorcore.getAllAncestors();
27589 if (this.formatCombo) {
27592 var store = this.formatCombo.store;
27593 this.formatCombo.setValue("");
27594 for (var i =0; i < ans.length;i++) {
27595 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27597 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27605 // hides menus... - so this cant be on a menu...
27606 Roo.bootstrap.MenuMgr.hideAll();
27608 Roo.bootstrap.MenuMgr.hideAll();
27609 //this.editorsyncValue();
27611 onFirstFocus: function() {
27612 this.buttons.each(function(item){
27616 toggleSourceEdit : function(sourceEditMode){
27619 if(sourceEditMode){
27620 Roo.log("disabling buttons");
27621 this.buttons.each( function(item){
27622 if(item.cmd != 'pencil'){
27628 Roo.log("enabling buttons");
27629 if(this.editorcore.initialized){
27630 this.buttons.each( function(item){
27636 Roo.log("calling toggole on editor");
27637 // tell the editor that it's been pressed..
27638 this.editor.toggleSourceEdit(sourceEditMode);
27652 * @class Roo.bootstrap.Markdown
27653 * @extends Roo.bootstrap.TextArea
27654 * Bootstrap Showdown editable area
27655 * @cfg {string} content
27658 * Create a new Showdown
27661 Roo.bootstrap.Markdown = function(config){
27662 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27666 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27670 initEvents : function()
27673 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27674 this.markdownEl = this.el.createChild({
27675 cls : 'roo-markdown-area'
27677 this.inputEl().addClass('d-none');
27678 if (this.getValue() == '') {
27679 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27682 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27684 this.markdownEl.on('click', this.toggleTextEdit, this);
27685 this.on('blur', this.toggleTextEdit, this);
27686 this.on('specialkey', this.resizeTextArea, this);
27689 toggleTextEdit : function()
27691 var sh = this.markdownEl.getHeight();
27692 this.inputEl().addClass('d-none');
27693 this.markdownEl.addClass('d-none');
27694 if (!this.editing) {
27696 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27697 this.inputEl().removeClass('d-none');
27698 this.inputEl().focus();
27699 this.editing = true;
27702 // show showdown...
27703 this.updateMarkdown();
27704 this.markdownEl.removeClass('d-none');
27705 this.editing = false;
27708 updateMarkdown : function()
27710 if (this.getValue() == '') {
27711 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27715 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27718 resizeTextArea: function () {
27721 Roo.log([sh, this.getValue().split("\n").length * 30]);
27722 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27724 setValue : function(val)
27726 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27727 if (!this.editing) {
27728 this.updateMarkdown();
27734 if (!this.editing) {
27735 this.toggleTextEdit();
27743 * Ext JS Library 1.1.1
27744 * Copyright(c) 2006-2007, Ext JS, LLC.
27746 * Originally Released Under LGPL - original licence link has changed is not relivant.
27749 * <script type="text/javascript">
27753 * @class Roo.bootstrap.PagingToolbar
27754 * @extends Roo.bootstrap.NavSimplebar
27755 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27757 * Create a new PagingToolbar
27758 * @param {Object} config The config object
27759 * @param {Roo.data.Store} store
27761 Roo.bootstrap.PagingToolbar = function(config)
27763 // old args format still supported... - xtype is prefered..
27764 // created from xtype...
27766 this.ds = config.dataSource;
27768 if (config.store && !this.ds) {
27769 this.store= Roo.factory(config.store, Roo.data);
27770 this.ds = this.store;
27771 this.ds.xmodule = this.xmodule || false;
27774 this.toolbarItems = [];
27775 if (config.items) {
27776 this.toolbarItems = config.items;
27779 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27784 this.bind(this.ds);
27787 if (Roo.bootstrap.version == 4) {
27788 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27790 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27795 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27797 * @cfg {Roo.data.Store} dataSource
27798 * The underlying data store providing the paged data
27801 * @cfg {String/HTMLElement/Element} container
27802 * container The id or element that will contain the toolbar
27805 * @cfg {Boolean} displayInfo
27806 * True to display the displayMsg (defaults to false)
27809 * @cfg {Number} pageSize
27810 * The number of records to display per page (defaults to 20)
27814 * @cfg {String} displayMsg
27815 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27817 displayMsg : 'Displaying {0} - {1} of {2}',
27819 * @cfg {String} emptyMsg
27820 * The message to display when no records are found (defaults to "No data to display")
27822 emptyMsg : 'No data to display',
27824 * Customizable piece of the default paging text (defaults to "Page")
27827 beforePageText : "Page",
27829 * Customizable piece of the default paging text (defaults to "of %0")
27832 afterPageText : "of {0}",
27834 * Customizable piece of the default paging text (defaults to "First Page")
27837 firstText : "First Page",
27839 * Customizable piece of the default paging text (defaults to "Previous Page")
27842 prevText : "Previous Page",
27844 * Customizable piece of the default paging text (defaults to "Next Page")
27847 nextText : "Next Page",
27849 * Customizable piece of the default paging text (defaults to "Last Page")
27852 lastText : "Last Page",
27854 * Customizable piece of the default paging text (defaults to "Refresh")
27857 refreshText : "Refresh",
27861 onRender : function(ct, position)
27863 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27864 this.navgroup.parentId = this.id;
27865 this.navgroup.onRender(this.el, null);
27866 // add the buttons to the navgroup
27868 if(this.displayInfo){
27869 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27870 this.displayEl = this.el.select('.x-paging-info', true).first();
27871 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27872 // this.displayEl = navel.el.select('span',true).first();
27878 Roo.each(_this.buttons, function(e){ // this might need to use render????
27879 Roo.factory(e).render(_this.el);
27883 Roo.each(_this.toolbarItems, function(e) {
27884 _this.navgroup.addItem(e);
27888 this.first = this.navgroup.addItem({
27889 tooltip: this.firstText,
27890 cls: "prev btn-outline-secondary",
27891 html : ' <i class="fa fa-step-backward"></i>',
27893 preventDefault: true,
27894 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27897 this.prev = this.navgroup.addItem({
27898 tooltip: this.prevText,
27899 cls: "prev btn-outline-secondary",
27900 html : ' <i class="fa fa-backward"></i>',
27902 preventDefault: true,
27903 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27905 //this.addSeparator();
27908 var field = this.navgroup.addItem( {
27910 cls : 'x-paging-position btn-outline-secondary',
27912 html : this.beforePageText +
27913 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27914 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27917 this.field = field.el.select('input', true).first();
27918 this.field.on("keydown", this.onPagingKeydown, this);
27919 this.field.on("focus", function(){this.dom.select();});
27922 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27923 //this.field.setHeight(18);
27924 //this.addSeparator();
27925 this.next = this.navgroup.addItem({
27926 tooltip: this.nextText,
27927 cls: "next btn-outline-secondary",
27928 html : ' <i class="fa fa-forward"></i>',
27930 preventDefault: true,
27931 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27933 this.last = this.navgroup.addItem({
27934 tooltip: this.lastText,
27935 html : ' <i class="fa fa-step-forward"></i>',
27936 cls: "next btn-outline-secondary",
27938 preventDefault: true,
27939 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27941 //this.addSeparator();
27942 this.loading = this.navgroup.addItem({
27943 tooltip: this.refreshText,
27944 cls: "btn-outline-secondary",
27945 html : ' <i class="fa fa-refresh"></i>',
27946 preventDefault: true,
27947 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27953 updateInfo : function(){
27954 if(this.displayEl){
27955 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27956 var msg = count == 0 ?
27960 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27962 this.displayEl.update(msg);
27967 onLoad : function(ds, r, o)
27969 this.cursor = o.params && o.params.start ? o.params.start : 0;
27971 var d = this.getPageData(),
27976 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27977 this.field.dom.value = ap;
27978 this.first.setDisabled(ap == 1);
27979 this.prev.setDisabled(ap == 1);
27980 this.next.setDisabled(ap == ps);
27981 this.last.setDisabled(ap == ps);
27982 this.loading.enable();
27987 getPageData : function(){
27988 var total = this.ds.getTotalCount();
27991 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27992 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27997 onLoadError : function(){
27998 this.loading.enable();
28002 onPagingKeydown : function(e){
28003 var k = e.getKey();
28004 var d = this.getPageData();
28006 var v = this.field.dom.value, pageNum;
28007 if(!v || isNaN(pageNum = parseInt(v, 10))){
28008 this.field.dom.value = d.activePage;
28011 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28012 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28015 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
28017 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28018 this.field.dom.value = pageNum;
28019 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28022 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28024 var v = this.field.dom.value, pageNum;
28025 var increment = (e.shiftKey) ? 10 : 1;
28026 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28029 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28030 this.field.dom.value = d.activePage;
28033 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28035 this.field.dom.value = parseInt(v, 10) + increment;
28036 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28037 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28044 beforeLoad : function(){
28046 this.loading.disable();
28051 onClick : function(which){
28060 ds.load({params:{start: 0, limit: this.pageSize}});
28063 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28066 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28069 var total = ds.getTotalCount();
28070 var extra = total % this.pageSize;
28071 var lastStart = extra ? (total - extra) : total-this.pageSize;
28072 ds.load({params:{start: lastStart, limit: this.pageSize}});
28075 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28081 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28082 * @param {Roo.data.Store} store The data store to unbind
28084 unbind : function(ds){
28085 ds.un("beforeload", this.beforeLoad, this);
28086 ds.un("load", this.onLoad, this);
28087 ds.un("loadexception", this.onLoadError, this);
28088 ds.un("remove", this.updateInfo, this);
28089 ds.un("add", this.updateInfo, this);
28090 this.ds = undefined;
28094 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28095 * @param {Roo.data.Store} store The data store to bind
28097 bind : function(ds){
28098 ds.on("beforeload", this.beforeLoad, this);
28099 ds.on("load", this.onLoad, this);
28100 ds.on("loadexception", this.onLoadError, this);
28101 ds.on("remove", this.updateInfo, this);
28102 ds.on("add", this.updateInfo, this);
28113 * @class Roo.bootstrap.MessageBar
28114 * @extends Roo.bootstrap.Component
28115 * Bootstrap MessageBar class
28116 * @cfg {String} html contents of the MessageBar
28117 * @cfg {String} weight (info | success | warning | danger) default info
28118 * @cfg {String} beforeClass insert the bar before the given class
28119 * @cfg {Boolean} closable (true | false) default false
28120 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28123 * Create a new Element
28124 * @param {Object} config The config object
28127 Roo.bootstrap.MessageBar = function(config){
28128 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28131 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28137 beforeClass: 'bootstrap-sticky-wrap',
28139 getAutoCreate : function(){
28143 cls: 'alert alert-dismissable alert-' + this.weight,
28148 html: this.html || ''
28154 cfg.cls += ' alert-messages-fixed';
28168 onRender : function(ct, position)
28170 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28173 var cfg = Roo.apply({}, this.getAutoCreate());
28177 cfg.cls += ' ' + this.cls;
28180 cfg.style = this.style;
28182 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28184 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28187 this.el.select('>button.close').on('click', this.hide, this);
28193 if (!this.rendered) {
28199 this.fireEvent('show', this);
28205 if (!this.rendered) {
28211 this.fireEvent('hide', this);
28214 update : function()
28216 // var e = this.el.dom.firstChild;
28218 // if(this.closable){
28219 // e = e.nextSibling;
28222 // e.data = this.html || '';
28224 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28240 * @class Roo.bootstrap.Graph
28241 * @extends Roo.bootstrap.Component
28242 * Bootstrap Graph class
28246 @cfg {String} graphtype bar | vbar | pie
28247 @cfg {number} g_x coodinator | centre x (pie)
28248 @cfg {number} g_y coodinator | centre y (pie)
28249 @cfg {number} g_r radius (pie)
28250 @cfg {number} g_height height of the chart (respected by all elements in the set)
28251 @cfg {number} g_width width of the chart (respected by all elements in the set)
28252 @cfg {Object} title The title of the chart
28255 -opts (object) options for the chart
28257 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28258 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28260 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
28261 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28263 o stretch (boolean)
28265 -opts (object) options for the pie
28268 o startAngle (number)
28269 o endAngle (number)
28273 * Create a new Input
28274 * @param {Object} config The config object
28277 Roo.bootstrap.Graph = function(config){
28278 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28284 * The img click event for the img.
28285 * @param {Roo.EventObject} e
28291 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28302 //g_colors: this.colors,
28309 getAutoCreate : function(){
28320 onRender : function(ct,position){
28323 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28325 if (typeof(Raphael) == 'undefined') {
28326 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28330 this.raphael = Raphael(this.el.dom);
28332 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28333 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28334 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28335 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28337 r.text(160, 10, "Single Series Chart").attr(txtattr);
28338 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28339 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28340 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28342 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28343 r.barchart(330, 10, 300, 220, data1);
28344 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28345 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28348 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28349 // r.barchart(30, 30, 560, 250, xdata, {
28350 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28351 // axis : "0 0 1 1",
28352 // axisxlabels : xdata
28353 // //yvalues : cols,
28356 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28358 // this.load(null,xdata,{
28359 // axis : "0 0 1 1",
28360 // axisxlabels : xdata
28365 load : function(graphtype,xdata,opts)
28367 this.raphael.clear();
28369 graphtype = this.graphtype;
28374 var r = this.raphael,
28375 fin = function () {
28376 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28378 fout = function () {
28379 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28381 pfin = function() {
28382 this.sector.stop();
28383 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28386 this.label[0].stop();
28387 this.label[0].attr({ r: 7.5 });
28388 this.label[1].attr({ "font-weight": 800 });
28391 pfout = function() {
28392 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28395 this.label[0].animate({ r: 5 }, 500, "bounce");
28396 this.label[1].attr({ "font-weight": 400 });
28402 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28405 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28408 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28409 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28411 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28418 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28423 setTitle: function(o)
28428 initEvents: function() {
28431 this.el.on('click', this.onClick, this);
28435 onClick : function(e)
28437 Roo.log('img onclick');
28438 this.fireEvent('click', this, e);
28450 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28453 * @class Roo.bootstrap.dash.NumberBox
28454 * @extends Roo.bootstrap.Component
28455 * Bootstrap NumberBox class
28456 * @cfg {String} headline Box headline
28457 * @cfg {String} content Box content
28458 * @cfg {String} icon Box icon
28459 * @cfg {String} footer Footer text
28460 * @cfg {String} fhref Footer href
28463 * Create a new NumberBox
28464 * @param {Object} config The config object
28468 Roo.bootstrap.dash.NumberBox = function(config){
28469 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28473 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28482 getAutoCreate : function(){
28486 cls : 'small-box ',
28494 cls : 'roo-headline',
28495 html : this.headline
28499 cls : 'roo-content',
28500 html : this.content
28514 cls : 'ion ' + this.icon
28523 cls : 'small-box-footer',
28524 href : this.fhref || '#',
28528 cfg.cn.push(footer);
28535 onRender : function(ct,position){
28536 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28543 setHeadline: function (value)
28545 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28548 setFooter: function (value, href)
28550 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28553 this.el.select('a.small-box-footer',true).first().attr('href', href);
28558 setContent: function (value)
28560 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28563 initEvents: function()
28577 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28580 * @class Roo.bootstrap.dash.TabBox
28581 * @extends Roo.bootstrap.Component
28582 * Bootstrap TabBox class
28583 * @cfg {String} title Title of the TabBox
28584 * @cfg {String} icon Icon of the TabBox
28585 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28586 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28589 * Create a new TabBox
28590 * @param {Object} config The config object
28594 Roo.bootstrap.dash.TabBox = function(config){
28595 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28600 * When a pane is added
28601 * @param {Roo.bootstrap.dash.TabPane} pane
28605 * @event activatepane
28606 * When a pane is activated
28607 * @param {Roo.bootstrap.dash.TabPane} pane
28609 "activatepane" : true
28617 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28622 tabScrollable : false,
28624 getChildContainer : function()
28626 return this.el.select('.tab-content', true).first();
28629 getAutoCreate : function(){
28633 cls: 'pull-left header',
28641 cls: 'fa ' + this.icon
28647 cls: 'nav nav-tabs pull-right',
28653 if(this.tabScrollable){
28660 cls: 'nav nav-tabs pull-right',
28671 cls: 'nav-tabs-custom',
28676 cls: 'tab-content no-padding',
28684 initEvents : function()
28686 //Roo.log('add add pane handler');
28687 this.on('addpane', this.onAddPane, this);
28690 * Updates the box title
28691 * @param {String} html to set the title to.
28693 setTitle : function(value)
28695 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28697 onAddPane : function(pane)
28699 this.panes.push(pane);
28700 //Roo.log('addpane');
28702 // tabs are rendere left to right..
28703 if(!this.showtabs){
28707 var ctr = this.el.select('.nav-tabs', true).first();
28710 var existing = ctr.select('.nav-tab',true);
28711 var qty = existing.getCount();;
28714 var tab = ctr.createChild({
28716 cls : 'nav-tab' + (qty ? '' : ' active'),
28724 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28727 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28729 pane.el.addClass('active');
28734 onTabClick : function(ev,un,ob,pane)
28736 //Roo.log('tab - prev default');
28737 ev.preventDefault();
28740 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28741 pane.tab.addClass('active');
28742 //Roo.log(pane.title);
28743 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28744 // technically we should have a deactivate event.. but maybe add later.
28745 // and it should not de-activate the selected tab...
28746 this.fireEvent('activatepane', pane);
28747 pane.el.addClass('active');
28748 pane.fireEvent('activate');
28753 getActivePane : function()
28756 Roo.each(this.panes, function(p) {
28757 if(p.el.hasClass('active')){
28778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28780 * @class Roo.bootstrap.TabPane
28781 * @extends Roo.bootstrap.Component
28782 * Bootstrap TabPane class
28783 * @cfg {Boolean} active (false | true) Default false
28784 * @cfg {String} title title of panel
28788 * Create a new TabPane
28789 * @param {Object} config The config object
28792 Roo.bootstrap.dash.TabPane = function(config){
28793 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28799 * When a pane is activated
28800 * @param {Roo.bootstrap.dash.TabPane} pane
28807 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28812 // the tabBox that this is attached to.
28815 getAutoCreate : function()
28823 cfg.cls += ' active';
28828 initEvents : function()
28830 //Roo.log('trigger add pane handler');
28831 this.parent().fireEvent('addpane', this)
28835 * Updates the tab title
28836 * @param {String} html to set the title to.
28838 setTitle: function(str)
28844 this.tab.select('a', true).first().dom.innerHTML = str;
28861 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28864 * @class Roo.bootstrap.menu.Menu
28865 * @extends Roo.bootstrap.Component
28866 * Bootstrap Menu class - container for Menu
28867 * @cfg {String} html Text of the menu
28868 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28869 * @cfg {String} icon Font awesome icon
28870 * @cfg {String} pos Menu align to (top | bottom) default bottom
28874 * Create a new Menu
28875 * @param {Object} config The config object
28879 Roo.bootstrap.menu.Menu = function(config){
28880 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28884 * @event beforeshow
28885 * Fires before this menu is displayed
28886 * @param {Roo.bootstrap.menu.Menu} this
28890 * @event beforehide
28891 * Fires before this menu is hidden
28892 * @param {Roo.bootstrap.menu.Menu} this
28897 * Fires after this menu is displayed
28898 * @param {Roo.bootstrap.menu.Menu} this
28903 * Fires after this menu is hidden
28904 * @param {Roo.bootstrap.menu.Menu} this
28909 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28910 * @param {Roo.bootstrap.menu.Menu} this
28911 * @param {Roo.EventObject} e
28918 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28922 weight : 'default',
28927 getChildContainer : function() {
28928 if(this.isSubMenu){
28932 return this.el.select('ul.dropdown-menu', true).first();
28935 getAutoCreate : function()
28940 cls : 'roo-menu-text',
28948 cls : 'fa ' + this.icon
28959 cls : 'dropdown-button btn btn-' + this.weight,
28964 cls : 'dropdown-toggle btn btn-' + this.weight,
28974 cls : 'dropdown-menu'
28980 if(this.pos == 'top'){
28981 cfg.cls += ' dropup';
28984 if(this.isSubMenu){
28987 cls : 'dropdown-menu'
28994 onRender : function(ct, position)
28996 this.isSubMenu = ct.hasClass('dropdown-submenu');
28998 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29001 initEvents : function()
29003 if(this.isSubMenu){
29007 this.hidden = true;
29009 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29010 this.triggerEl.on('click', this.onTriggerPress, this);
29012 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29013 this.buttonEl.on('click', this.onClick, this);
29019 if(this.isSubMenu){
29023 return this.el.select('ul.dropdown-menu', true).first();
29026 onClick : function(e)
29028 this.fireEvent("click", this, e);
29031 onTriggerPress : function(e)
29033 if (this.isVisible()) {
29040 isVisible : function(){
29041 return !this.hidden;
29046 this.fireEvent("beforeshow", this);
29048 this.hidden = false;
29049 this.el.addClass('open');
29051 Roo.get(document).on("mouseup", this.onMouseUp, this);
29053 this.fireEvent("show", this);
29060 this.fireEvent("beforehide", this);
29062 this.hidden = true;
29063 this.el.removeClass('open');
29065 Roo.get(document).un("mouseup", this.onMouseUp);
29067 this.fireEvent("hide", this);
29070 onMouseUp : function()
29084 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29087 * @class Roo.bootstrap.menu.Item
29088 * @extends Roo.bootstrap.Component
29089 * Bootstrap MenuItem class
29090 * @cfg {Boolean} submenu (true | false) default false
29091 * @cfg {String} html text of the item
29092 * @cfg {String} href the link
29093 * @cfg {Boolean} disable (true | false) default false
29094 * @cfg {Boolean} preventDefault (true | false) default true
29095 * @cfg {String} icon Font awesome icon
29096 * @cfg {String} pos Submenu align to (left | right) default right
29100 * Create a new Item
29101 * @param {Object} config The config object
29105 Roo.bootstrap.menu.Item = function(config){
29106 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29110 * Fires when the mouse is hovering over this menu
29111 * @param {Roo.bootstrap.menu.Item} this
29112 * @param {Roo.EventObject} e
29117 * Fires when the mouse exits this menu
29118 * @param {Roo.bootstrap.menu.Item} this
29119 * @param {Roo.EventObject} e
29125 * The raw click event for the entire grid.
29126 * @param {Roo.EventObject} e
29132 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29137 preventDefault: true,
29142 getAutoCreate : function()
29147 cls : 'roo-menu-item-text',
29155 cls : 'fa ' + this.icon
29164 href : this.href || '#',
29171 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29175 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29177 if(this.pos == 'left'){
29178 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29185 initEvents : function()
29187 this.el.on('mouseover', this.onMouseOver, this);
29188 this.el.on('mouseout', this.onMouseOut, this);
29190 this.el.select('a', true).first().on('click', this.onClick, this);
29194 onClick : function(e)
29196 if(this.preventDefault){
29197 e.preventDefault();
29200 this.fireEvent("click", this, e);
29203 onMouseOver : function(e)
29205 if(this.submenu && this.pos == 'left'){
29206 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29209 this.fireEvent("mouseover", this, e);
29212 onMouseOut : function(e)
29214 this.fireEvent("mouseout", this, e);
29226 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29229 * @class Roo.bootstrap.menu.Separator
29230 * @extends Roo.bootstrap.Component
29231 * Bootstrap Separator class
29234 * Create a new Separator
29235 * @param {Object} config The config object
29239 Roo.bootstrap.menu.Separator = function(config){
29240 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29243 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29245 getAutoCreate : function(){
29248 cls: 'dropdown-divider divider'
29266 * @class Roo.bootstrap.Tooltip
29267 * Bootstrap Tooltip class
29268 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29269 * to determine which dom element triggers the tooltip.
29271 * It needs to add support for additional attributes like tooltip-position
29274 * Create a new Toolti
29275 * @param {Object} config The config object
29278 Roo.bootstrap.Tooltip = function(config){
29279 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29281 this.alignment = Roo.bootstrap.Tooltip.alignment;
29283 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29284 this.alignment = config.alignment;
29289 Roo.apply(Roo.bootstrap.Tooltip, {
29291 * @function init initialize tooltip monitoring.
29295 currentTip : false,
29296 currentRegion : false,
29302 Roo.get(document).on('mouseover', this.enter ,this);
29303 Roo.get(document).on('mouseout', this.leave, this);
29306 this.currentTip = new Roo.bootstrap.Tooltip();
29309 enter : function(ev)
29311 var dom = ev.getTarget();
29313 //Roo.log(['enter',dom]);
29314 var el = Roo.fly(dom);
29315 if (this.currentEl) {
29317 //Roo.log(this.currentEl);
29318 //Roo.log(this.currentEl.contains(dom));
29319 if (this.currentEl == el) {
29322 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29328 if (this.currentTip.el) {
29329 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29333 if(!el || el.dom == document){
29339 if (!el.attr('tooltip')) {
29340 pel = el.findParent("[tooltip]");
29342 bindEl = Roo.get(pel);
29348 // you can not look for children, as if el is the body.. then everythign is the child..
29349 if (!pel && !el.attr('tooltip')) { //
29350 if (!el.select("[tooltip]").elements.length) {
29353 // is the mouse over this child...?
29354 bindEl = el.select("[tooltip]").first();
29355 var xy = ev.getXY();
29356 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29357 //Roo.log("not in region.");
29360 //Roo.log("child element over..");
29363 this.currentEl = el;
29364 this.currentTip.bind(bindEl);
29365 this.currentRegion = Roo.lib.Region.getRegion(dom);
29366 this.currentTip.enter();
29369 leave : function(ev)
29371 var dom = ev.getTarget();
29372 //Roo.log(['leave',dom]);
29373 if (!this.currentEl) {
29378 if (dom != this.currentEl.dom) {
29381 var xy = ev.getXY();
29382 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29385 // only activate leave if mouse cursor is outside... bounding box..
29390 if (this.currentTip) {
29391 this.currentTip.leave();
29393 //Roo.log('clear currentEl');
29394 this.currentEl = false;
29399 'left' : ['r-l', [-2,0], 'right'],
29400 'right' : ['l-r', [2,0], 'left'],
29401 'bottom' : ['t-b', [0,2], 'top'],
29402 'top' : [ 'b-t', [0,-2], 'bottom']
29408 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29413 delay : null, // can be { show : 300 , hide: 500}
29417 hoverState : null, //???
29419 placement : 'bottom',
29423 getAutoCreate : function(){
29430 cls : 'tooltip-arrow arrow'
29433 cls : 'tooltip-inner'
29440 bind : function(el)
29445 initEvents : function()
29447 this.arrowEl = this.el.select('.arrow', true).first();
29448 this.innerEl = this.el.select('.tooltip-inner', true).first();
29451 enter : function () {
29453 if (this.timeout != null) {
29454 clearTimeout(this.timeout);
29457 this.hoverState = 'in';
29458 //Roo.log("enter - show");
29459 if (!this.delay || !this.delay.show) {
29464 this.timeout = setTimeout(function () {
29465 if (_t.hoverState == 'in') {
29468 }, this.delay.show);
29472 clearTimeout(this.timeout);
29474 this.hoverState = 'out';
29475 if (!this.delay || !this.delay.hide) {
29481 this.timeout = setTimeout(function () {
29482 //Roo.log("leave - timeout");
29484 if (_t.hoverState == 'out') {
29486 Roo.bootstrap.Tooltip.currentEl = false;
29491 show : function (msg)
29494 this.render(document.body);
29497 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29499 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29501 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29503 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29504 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29506 var placement = typeof this.placement == 'function' ?
29507 this.placement.call(this, this.el, on_el) :
29510 var autoToken = /\s?auto?\s?/i;
29511 var autoPlace = autoToken.test(placement);
29513 placement = placement.replace(autoToken, '') || 'top';
29517 //this.el.setXY([0,0]);
29519 //this.el.dom.style.display='block';
29521 //this.el.appendTo(on_el);
29523 var p = this.getPosition();
29524 var box = this.el.getBox();
29530 var align = this.alignment[placement];
29532 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29534 if(placement == 'top' || placement == 'bottom'){
29536 placement = 'right';
29539 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29540 placement = 'left';
29543 var scroll = Roo.select('body', true).first().getScroll();
29545 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29549 align = this.alignment[placement];
29551 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29555 var elems = document.getElementsByTagName('div');
29556 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29557 for (var i = 0; i < elems.length; i++) {
29558 var zindex = Number.parseInt(
29559 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29562 if (zindex > highest) {
29569 this.el.dom.style.zIndex = highest;
29571 this.el.alignTo(this.bindEl, align[0],align[1]);
29572 //var arrow = this.el.select('.arrow',true).first();
29573 //arrow.set(align[2],
29575 this.el.addClass(placement);
29576 this.el.addClass("bs-tooltip-"+ placement);
29578 this.el.addClass('in fade show');
29580 this.hoverState = null;
29582 if (this.el.hasClass('fade')) {
29597 //this.el.setXY([0,0]);
29598 this.el.removeClass(['show', 'in']);
29614 * @class Roo.bootstrap.LocationPicker
29615 * @extends Roo.bootstrap.Component
29616 * Bootstrap LocationPicker class
29617 * @cfg {Number} latitude Position when init default 0
29618 * @cfg {Number} longitude Position when init default 0
29619 * @cfg {Number} zoom default 15
29620 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29621 * @cfg {Boolean} mapTypeControl default false
29622 * @cfg {Boolean} disableDoubleClickZoom default false
29623 * @cfg {Boolean} scrollwheel default true
29624 * @cfg {Boolean} streetViewControl default false
29625 * @cfg {Number} radius default 0
29626 * @cfg {String} locationName
29627 * @cfg {Boolean} draggable default true
29628 * @cfg {Boolean} enableAutocomplete default false
29629 * @cfg {Boolean} enableReverseGeocode default true
29630 * @cfg {String} markerTitle
29633 * Create a new LocationPicker
29634 * @param {Object} config The config object
29638 Roo.bootstrap.LocationPicker = function(config){
29640 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29645 * Fires when the picker initialized.
29646 * @param {Roo.bootstrap.LocationPicker} this
29647 * @param {Google Location} location
29651 * @event positionchanged
29652 * Fires when the picker position changed.
29653 * @param {Roo.bootstrap.LocationPicker} this
29654 * @param {Google Location} location
29656 positionchanged : true,
29659 * Fires when the map resize.
29660 * @param {Roo.bootstrap.LocationPicker} this
29665 * Fires when the map show.
29666 * @param {Roo.bootstrap.LocationPicker} this
29671 * Fires when the map hide.
29672 * @param {Roo.bootstrap.LocationPicker} this
29677 * Fires when click the map.
29678 * @param {Roo.bootstrap.LocationPicker} this
29679 * @param {Map event} e
29683 * @event mapRightClick
29684 * Fires when right click the map.
29685 * @param {Roo.bootstrap.LocationPicker} this
29686 * @param {Map event} e
29688 mapRightClick : true,
29690 * @event markerClick
29691 * Fires when click the marker.
29692 * @param {Roo.bootstrap.LocationPicker} this
29693 * @param {Map event} e
29695 markerClick : true,
29697 * @event markerRightClick
29698 * Fires when right click the marker.
29699 * @param {Roo.bootstrap.LocationPicker} this
29700 * @param {Map event} e
29702 markerRightClick : true,
29704 * @event OverlayViewDraw
29705 * Fires when OverlayView Draw
29706 * @param {Roo.bootstrap.LocationPicker} this
29708 OverlayViewDraw : true,
29710 * @event OverlayViewOnAdd
29711 * Fires when OverlayView Draw
29712 * @param {Roo.bootstrap.LocationPicker} this
29714 OverlayViewOnAdd : true,
29716 * @event OverlayViewOnRemove
29717 * Fires when OverlayView Draw
29718 * @param {Roo.bootstrap.LocationPicker} this
29720 OverlayViewOnRemove : true,
29722 * @event OverlayViewShow
29723 * Fires when OverlayView Draw
29724 * @param {Roo.bootstrap.LocationPicker} this
29725 * @param {Pixel} cpx
29727 OverlayViewShow : true,
29729 * @event OverlayViewHide
29730 * Fires when OverlayView Draw
29731 * @param {Roo.bootstrap.LocationPicker} this
29733 OverlayViewHide : true,
29735 * @event loadexception
29736 * Fires when load google lib failed.
29737 * @param {Roo.bootstrap.LocationPicker} this
29739 loadexception : true
29744 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29746 gMapContext: false,
29752 mapTypeControl: false,
29753 disableDoubleClickZoom: false,
29755 streetViewControl: false,
29759 enableAutocomplete: false,
29760 enableReverseGeocode: true,
29763 getAutoCreate: function()
29768 cls: 'roo-location-picker'
29774 initEvents: function(ct, position)
29776 if(!this.el.getWidth() || this.isApplied()){
29780 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29785 initial: function()
29787 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29788 this.fireEvent('loadexception', this);
29792 if(!this.mapTypeId){
29793 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29796 this.gMapContext = this.GMapContext();
29798 this.initOverlayView();
29800 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29804 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29805 _this.setPosition(_this.gMapContext.marker.position);
29808 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29809 _this.fireEvent('mapClick', this, event);
29813 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29814 _this.fireEvent('mapRightClick', this, event);
29818 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29819 _this.fireEvent('markerClick', this, event);
29823 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29824 _this.fireEvent('markerRightClick', this, event);
29828 this.setPosition(this.gMapContext.location);
29830 this.fireEvent('initial', this, this.gMapContext.location);
29833 initOverlayView: function()
29837 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29841 _this.fireEvent('OverlayViewDraw', _this);
29846 _this.fireEvent('OverlayViewOnAdd', _this);
29849 onRemove: function()
29851 _this.fireEvent('OverlayViewOnRemove', _this);
29854 show: function(cpx)
29856 _this.fireEvent('OverlayViewShow', _this, cpx);
29861 _this.fireEvent('OverlayViewHide', _this);
29867 fromLatLngToContainerPixel: function(event)
29869 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29872 isApplied: function()
29874 return this.getGmapContext() == false ? false : true;
29877 getGmapContext: function()
29879 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29882 GMapContext: function()
29884 var position = new google.maps.LatLng(this.latitude, this.longitude);
29886 var _map = new google.maps.Map(this.el.dom, {
29889 mapTypeId: this.mapTypeId,
29890 mapTypeControl: this.mapTypeControl,
29891 disableDoubleClickZoom: this.disableDoubleClickZoom,
29892 scrollwheel: this.scrollwheel,
29893 streetViewControl: this.streetViewControl,
29894 locationName: this.locationName,
29895 draggable: this.draggable,
29896 enableAutocomplete: this.enableAutocomplete,
29897 enableReverseGeocode: this.enableReverseGeocode
29900 var _marker = new google.maps.Marker({
29901 position: position,
29903 title: this.markerTitle,
29904 draggable: this.draggable
29911 location: position,
29912 radius: this.radius,
29913 locationName: this.locationName,
29914 addressComponents: {
29915 formatted_address: null,
29916 addressLine1: null,
29917 addressLine2: null,
29919 streetNumber: null,
29923 stateOrProvince: null
29926 domContainer: this.el.dom,
29927 geodecoder: new google.maps.Geocoder()
29931 drawCircle: function(center, radius, options)
29933 if (this.gMapContext.circle != null) {
29934 this.gMapContext.circle.setMap(null);
29938 options = Roo.apply({}, options, {
29939 strokeColor: "#0000FF",
29940 strokeOpacity: .35,
29942 fillColor: "#0000FF",
29946 options.map = this.gMapContext.map;
29947 options.radius = radius;
29948 options.center = center;
29949 this.gMapContext.circle = new google.maps.Circle(options);
29950 return this.gMapContext.circle;
29956 setPosition: function(location)
29958 this.gMapContext.location = location;
29959 this.gMapContext.marker.setPosition(location);
29960 this.gMapContext.map.panTo(location);
29961 this.drawCircle(location, this.gMapContext.radius, {});
29965 if (this.gMapContext.settings.enableReverseGeocode) {
29966 this.gMapContext.geodecoder.geocode({
29967 latLng: this.gMapContext.location
29968 }, function(results, status) {
29970 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29971 _this.gMapContext.locationName = results[0].formatted_address;
29972 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29974 _this.fireEvent('positionchanged', this, location);
29981 this.fireEvent('positionchanged', this, location);
29986 google.maps.event.trigger(this.gMapContext.map, "resize");
29988 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29990 this.fireEvent('resize', this);
29993 setPositionByLatLng: function(latitude, longitude)
29995 this.setPosition(new google.maps.LatLng(latitude, longitude));
29998 getCurrentPosition: function()
30001 latitude: this.gMapContext.location.lat(),
30002 longitude: this.gMapContext.location.lng()
30006 getAddressName: function()
30008 return this.gMapContext.locationName;
30011 getAddressComponents: function()
30013 return this.gMapContext.addressComponents;
30016 address_component_from_google_geocode: function(address_components)
30020 for (var i = 0; i < address_components.length; i++) {
30021 var component = address_components[i];
30022 if (component.types.indexOf("postal_code") >= 0) {
30023 result.postalCode = component.short_name;
30024 } else if (component.types.indexOf("street_number") >= 0) {
30025 result.streetNumber = component.short_name;
30026 } else if (component.types.indexOf("route") >= 0) {
30027 result.streetName = component.short_name;
30028 } else if (component.types.indexOf("neighborhood") >= 0) {
30029 result.city = component.short_name;
30030 } else if (component.types.indexOf("locality") >= 0) {
30031 result.city = component.short_name;
30032 } else if (component.types.indexOf("sublocality") >= 0) {
30033 result.district = component.short_name;
30034 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30035 result.stateOrProvince = component.short_name;
30036 } else if (component.types.indexOf("country") >= 0) {
30037 result.country = component.short_name;
30041 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30042 result.addressLine2 = "";
30046 setZoomLevel: function(zoom)
30048 this.gMapContext.map.setZoom(zoom);
30061 this.fireEvent('show', this);
30072 this.fireEvent('hide', this);
30077 Roo.apply(Roo.bootstrap.LocationPicker, {
30079 OverlayView : function(map, options)
30081 options = options || {};
30088 * @class Roo.bootstrap.Alert
30089 * @extends Roo.bootstrap.Component
30090 * Bootstrap Alert class - shows an alert area box
30092 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30093 Enter a valid email address
30096 * @cfg {String} title The title of alert
30097 * @cfg {String} html The content of alert
30098 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30099 * @cfg {String} fa font-awesomeicon
30100 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30101 * @cfg {Boolean} close true to show a x closer
30105 * Create a new alert
30106 * @param {Object} config The config object
30110 Roo.bootstrap.Alert = function(config){
30111 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30115 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30121 faicon: false, // BC
30125 getAutoCreate : function()
30137 style : this.close ? '' : 'display:none'
30141 cls : 'roo-alert-icon'
30146 cls : 'roo-alert-title',
30151 cls : 'roo-alert-text',
30158 cfg.cn[0].cls += ' fa ' + this.faicon;
30161 cfg.cn[0].cls += ' fa ' + this.fa;
30165 cfg.cls += ' alert-' + this.weight;
30171 initEvents: function()
30173 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30174 this.titleEl = this.el.select('.roo-alert-title',true).first();
30175 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30176 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30177 if (this.seconds > 0) {
30178 this.hide.defer(this.seconds, this);
30182 * Set the Title Message HTML
30183 * @param {String} html
30185 setTitle : function(str)
30187 this.titleEl.dom.innerHTML = str;
30191 * Set the Body Message HTML
30192 * @param {String} html
30194 setHtml : function(str)
30196 this.htmlEl.dom.innerHTML = str;
30199 * Set the Weight of the alert
30200 * @param {String} (success|info|warning|danger) weight
30203 setWeight : function(weight)
30206 this.el.removeClass('alert-' + this.weight);
30209 this.weight = weight;
30211 this.el.addClass('alert-' + this.weight);
30214 * Set the Icon of the alert
30215 * @param {String} see fontawsome names (name without the 'fa-' bit)
30217 setIcon : function(icon)
30220 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30223 this.faicon = icon;
30225 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30250 * @class Roo.bootstrap.UploadCropbox
30251 * @extends Roo.bootstrap.Component
30252 * Bootstrap UploadCropbox class
30253 * @cfg {String} emptyText show when image has been loaded
30254 * @cfg {String} rotateNotify show when image too small to rotate
30255 * @cfg {Number} errorTimeout default 3000
30256 * @cfg {Number} minWidth default 300
30257 * @cfg {Number} minHeight default 300
30258 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30259 * @cfg {Boolean} isDocument (true|false) default false
30260 * @cfg {String} url action url
30261 * @cfg {String} paramName default 'imageUpload'
30262 * @cfg {String} method default POST
30263 * @cfg {Boolean} loadMask (true|false) default true
30264 * @cfg {Boolean} loadingText default 'Loading...'
30267 * Create a new UploadCropbox
30268 * @param {Object} config The config object
30271 Roo.bootstrap.UploadCropbox = function(config){
30272 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30276 * @event beforeselectfile
30277 * Fire before select file
30278 * @param {Roo.bootstrap.UploadCropbox} this
30280 "beforeselectfile" : true,
30283 * Fire after initEvent
30284 * @param {Roo.bootstrap.UploadCropbox} this
30289 * Fire after initEvent
30290 * @param {Roo.bootstrap.UploadCropbox} this
30291 * @param {String} data
30296 * Fire when preparing the file data
30297 * @param {Roo.bootstrap.UploadCropbox} this
30298 * @param {Object} file
30303 * Fire when get exception
30304 * @param {Roo.bootstrap.UploadCropbox} this
30305 * @param {XMLHttpRequest} xhr
30307 "exception" : true,
30309 * @event beforeloadcanvas
30310 * Fire before load the canvas
30311 * @param {Roo.bootstrap.UploadCropbox} this
30312 * @param {String} src
30314 "beforeloadcanvas" : true,
30317 * Fire when trash image
30318 * @param {Roo.bootstrap.UploadCropbox} this
30323 * Fire when download the image
30324 * @param {Roo.bootstrap.UploadCropbox} this
30328 * @event footerbuttonclick
30329 * Fire when footerbuttonclick
30330 * @param {Roo.bootstrap.UploadCropbox} this
30331 * @param {String} type
30333 "footerbuttonclick" : true,
30337 * @param {Roo.bootstrap.UploadCropbox} this
30342 * Fire when rotate the image
30343 * @param {Roo.bootstrap.UploadCropbox} this
30344 * @param {String} pos
30349 * Fire when inspect the file
30350 * @param {Roo.bootstrap.UploadCropbox} this
30351 * @param {Object} file
30356 * Fire when xhr upload the file
30357 * @param {Roo.bootstrap.UploadCropbox} this
30358 * @param {Object} data
30363 * Fire when arrange the file data
30364 * @param {Roo.bootstrap.UploadCropbox} this
30365 * @param {Object} formData
30370 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30373 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30375 emptyText : 'Click to upload image',
30376 rotateNotify : 'Image is too small to rotate',
30377 errorTimeout : 3000,
30391 cropType : 'image/jpeg',
30393 canvasLoaded : false,
30394 isDocument : false,
30396 paramName : 'imageUpload',
30398 loadingText : 'Loading...',
30401 getAutoCreate : function()
30405 cls : 'roo-upload-cropbox',
30409 cls : 'roo-upload-cropbox-selector',
30414 cls : 'roo-upload-cropbox-body',
30415 style : 'cursor:pointer',
30419 cls : 'roo-upload-cropbox-preview'
30423 cls : 'roo-upload-cropbox-thumb'
30427 cls : 'roo-upload-cropbox-empty-notify',
30428 html : this.emptyText
30432 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30433 html : this.rotateNotify
30439 cls : 'roo-upload-cropbox-footer',
30442 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30452 onRender : function(ct, position)
30454 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30456 if (this.buttons.length) {
30458 Roo.each(this.buttons, function(bb) {
30460 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30462 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30468 this.maskEl = this.el;
30472 initEvents : function()
30474 this.urlAPI = (window.createObjectURL && window) ||
30475 (window.URL && URL.revokeObjectURL && URL) ||
30476 (window.webkitURL && webkitURL);
30478 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30479 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30481 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30482 this.selectorEl.hide();
30484 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30485 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30487 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30488 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30489 this.thumbEl.hide();
30491 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30492 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30494 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30495 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30496 this.errorEl.hide();
30498 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30499 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30500 this.footerEl.hide();
30502 this.setThumbBoxSize();
30508 this.fireEvent('initial', this);
30515 window.addEventListener("resize", function() { _this.resize(); } );
30517 this.bodyEl.on('click', this.beforeSelectFile, this);
30520 this.bodyEl.on('touchstart', this.onTouchStart, this);
30521 this.bodyEl.on('touchmove', this.onTouchMove, this);
30522 this.bodyEl.on('touchend', this.onTouchEnd, this);
30526 this.bodyEl.on('mousedown', this.onMouseDown, this);
30527 this.bodyEl.on('mousemove', this.onMouseMove, this);
30528 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30529 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30530 Roo.get(document).on('mouseup', this.onMouseUp, this);
30533 this.selectorEl.on('change', this.onFileSelected, this);
30539 this.baseScale = 1;
30541 this.baseRotate = 1;
30542 this.dragable = false;
30543 this.pinching = false;
30546 this.cropData = false;
30547 this.notifyEl.dom.innerHTML = this.emptyText;
30549 this.selectorEl.dom.value = '';
30553 resize : function()
30555 if(this.fireEvent('resize', this) != false){
30556 this.setThumbBoxPosition();
30557 this.setCanvasPosition();
30561 onFooterButtonClick : function(e, el, o, type)
30564 case 'rotate-left' :
30565 this.onRotateLeft(e);
30567 case 'rotate-right' :
30568 this.onRotateRight(e);
30571 this.beforeSelectFile(e);
30586 this.fireEvent('footerbuttonclick', this, type);
30589 beforeSelectFile : function(e)
30591 e.preventDefault();
30593 if(this.fireEvent('beforeselectfile', this) != false){
30594 this.selectorEl.dom.click();
30598 onFileSelected : function(e)
30600 e.preventDefault();
30602 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30606 var file = this.selectorEl.dom.files[0];
30608 if(this.fireEvent('inspect', this, file) != false){
30609 this.prepare(file);
30614 trash : function(e)
30616 this.fireEvent('trash', this);
30619 download : function(e)
30621 this.fireEvent('download', this);
30624 loadCanvas : function(src)
30626 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30630 this.imageEl = document.createElement('img');
30634 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30636 this.imageEl.src = src;
30640 onLoadCanvas : function()
30642 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30643 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30645 this.bodyEl.un('click', this.beforeSelectFile, this);
30647 this.notifyEl.hide();
30648 this.thumbEl.show();
30649 this.footerEl.show();
30651 this.baseRotateLevel();
30653 if(this.isDocument){
30654 this.setThumbBoxSize();
30657 this.setThumbBoxPosition();
30659 this.baseScaleLevel();
30665 this.canvasLoaded = true;
30668 this.maskEl.unmask();
30673 setCanvasPosition : function()
30675 if(!this.canvasEl){
30679 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30680 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30682 this.previewEl.setLeft(pw);
30683 this.previewEl.setTop(ph);
30687 onMouseDown : function(e)
30691 this.dragable = true;
30692 this.pinching = false;
30694 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30695 this.dragable = false;
30699 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30700 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30704 onMouseMove : function(e)
30708 if(!this.canvasLoaded){
30712 if (!this.dragable){
30716 var minX = Math.ceil(this.thumbEl.getLeft(true));
30717 var minY = Math.ceil(this.thumbEl.getTop(true));
30719 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30720 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30722 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30723 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30725 x = x - this.mouseX;
30726 y = y - this.mouseY;
30728 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30729 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30731 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30732 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30734 this.previewEl.setLeft(bgX);
30735 this.previewEl.setTop(bgY);
30737 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30738 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30741 onMouseUp : function(e)
30745 this.dragable = false;
30748 onMouseWheel : function(e)
30752 this.startScale = this.scale;
30754 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30756 if(!this.zoomable()){
30757 this.scale = this.startScale;
30766 zoomable : function()
30768 var minScale = this.thumbEl.getWidth() / this.minWidth;
30770 if(this.minWidth < this.minHeight){
30771 minScale = this.thumbEl.getHeight() / this.minHeight;
30774 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30775 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30779 (this.rotate == 0 || this.rotate == 180) &&
30781 width > this.imageEl.OriginWidth ||
30782 height > this.imageEl.OriginHeight ||
30783 (width < this.minWidth && height < this.minHeight)
30791 (this.rotate == 90 || this.rotate == 270) &&
30793 width > this.imageEl.OriginWidth ||
30794 height > this.imageEl.OriginHeight ||
30795 (width < this.minHeight && height < this.minWidth)
30802 !this.isDocument &&
30803 (this.rotate == 0 || this.rotate == 180) &&
30805 width < this.minWidth ||
30806 width > this.imageEl.OriginWidth ||
30807 height < this.minHeight ||
30808 height > this.imageEl.OriginHeight
30815 !this.isDocument &&
30816 (this.rotate == 90 || this.rotate == 270) &&
30818 width < this.minHeight ||
30819 width > this.imageEl.OriginWidth ||
30820 height < this.minWidth ||
30821 height > this.imageEl.OriginHeight
30831 onRotateLeft : function(e)
30833 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30835 var minScale = this.thumbEl.getWidth() / this.minWidth;
30837 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30838 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30840 this.startScale = this.scale;
30842 while (this.getScaleLevel() < minScale){
30844 this.scale = this.scale + 1;
30846 if(!this.zoomable()){
30851 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30852 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30857 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30864 this.scale = this.startScale;
30866 this.onRotateFail();
30871 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30873 if(this.isDocument){
30874 this.setThumbBoxSize();
30875 this.setThumbBoxPosition();
30876 this.setCanvasPosition();
30881 this.fireEvent('rotate', this, 'left');
30885 onRotateRight : function(e)
30887 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30889 var minScale = this.thumbEl.getWidth() / this.minWidth;
30891 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30892 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30894 this.startScale = this.scale;
30896 while (this.getScaleLevel() < minScale){
30898 this.scale = this.scale + 1;
30900 if(!this.zoomable()){
30905 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30906 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30911 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30918 this.scale = this.startScale;
30920 this.onRotateFail();
30925 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30927 if(this.isDocument){
30928 this.setThumbBoxSize();
30929 this.setThumbBoxPosition();
30930 this.setCanvasPosition();
30935 this.fireEvent('rotate', this, 'right');
30938 onRotateFail : function()
30940 this.errorEl.show(true);
30944 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30949 this.previewEl.dom.innerHTML = '';
30951 var canvasEl = document.createElement("canvas");
30953 var contextEl = canvasEl.getContext("2d");
30955 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30956 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30957 var center = this.imageEl.OriginWidth / 2;
30959 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30960 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30961 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30962 center = this.imageEl.OriginHeight / 2;
30965 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30967 contextEl.translate(center, center);
30968 contextEl.rotate(this.rotate * Math.PI / 180);
30970 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30972 this.canvasEl = document.createElement("canvas");
30974 this.contextEl = this.canvasEl.getContext("2d");
30976 switch (this.rotate) {
30979 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30980 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30982 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30987 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30988 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30990 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30991 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30995 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31000 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31001 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31003 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31004 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31008 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31013 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31014 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31016 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31017 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31021 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31028 this.previewEl.appendChild(this.canvasEl);
31030 this.setCanvasPosition();
31035 if(!this.canvasLoaded){
31039 var imageCanvas = document.createElement("canvas");
31041 var imageContext = imageCanvas.getContext("2d");
31043 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31044 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31046 var center = imageCanvas.width / 2;
31048 imageContext.translate(center, center);
31050 imageContext.rotate(this.rotate * Math.PI / 180);
31052 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31054 var canvas = document.createElement("canvas");
31056 var context = canvas.getContext("2d");
31058 canvas.width = this.minWidth;
31059 canvas.height = this.minHeight;
31061 switch (this.rotate) {
31064 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31065 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31067 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31068 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31070 var targetWidth = this.minWidth - 2 * x;
31071 var targetHeight = this.minHeight - 2 * y;
31075 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31076 scale = targetWidth / width;
31079 if(x > 0 && y == 0){
31080 scale = targetHeight / height;
31083 if(x > 0 && y > 0){
31084 scale = targetWidth / width;
31086 if(width < height){
31087 scale = targetHeight / height;
31091 context.scale(scale, scale);
31093 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31094 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31096 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31097 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31099 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31104 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31105 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31107 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31108 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31110 var targetWidth = this.minWidth - 2 * x;
31111 var targetHeight = this.minHeight - 2 * y;
31115 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31116 scale = targetWidth / width;
31119 if(x > 0 && y == 0){
31120 scale = targetHeight / height;
31123 if(x > 0 && y > 0){
31124 scale = targetWidth / width;
31126 if(width < height){
31127 scale = targetHeight / height;
31131 context.scale(scale, scale);
31133 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31134 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31136 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31137 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31139 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31141 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31146 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31147 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31149 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31150 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31152 var targetWidth = this.minWidth - 2 * x;
31153 var targetHeight = this.minHeight - 2 * y;
31157 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31158 scale = targetWidth / width;
31161 if(x > 0 && y == 0){
31162 scale = targetHeight / height;
31165 if(x > 0 && y > 0){
31166 scale = targetWidth / width;
31168 if(width < height){
31169 scale = targetHeight / height;
31173 context.scale(scale, scale);
31175 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31176 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31178 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31179 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31181 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31182 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31184 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31189 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31190 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31192 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31193 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31195 var targetWidth = this.minWidth - 2 * x;
31196 var targetHeight = this.minHeight - 2 * y;
31200 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31201 scale = targetWidth / width;
31204 if(x > 0 && y == 0){
31205 scale = targetHeight / height;
31208 if(x > 0 && y > 0){
31209 scale = targetWidth / width;
31211 if(width < height){
31212 scale = targetHeight / height;
31216 context.scale(scale, scale);
31218 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31219 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31221 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31222 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31224 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31226 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31233 this.cropData = canvas.toDataURL(this.cropType);
31235 if(this.fireEvent('crop', this, this.cropData) !== false){
31236 this.process(this.file, this.cropData);
31243 setThumbBoxSize : function()
31247 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31248 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31249 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31251 this.minWidth = width;
31252 this.minHeight = height;
31254 if(this.rotate == 90 || this.rotate == 270){
31255 this.minWidth = height;
31256 this.minHeight = width;
31261 width = Math.ceil(this.minWidth * height / this.minHeight);
31263 if(this.minWidth > this.minHeight){
31265 height = Math.ceil(this.minHeight * width / this.minWidth);
31268 this.thumbEl.setStyle({
31269 width : width + 'px',
31270 height : height + 'px'
31277 setThumbBoxPosition : function()
31279 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31280 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31282 this.thumbEl.setLeft(x);
31283 this.thumbEl.setTop(y);
31287 baseRotateLevel : function()
31289 this.baseRotate = 1;
31292 typeof(this.exif) != 'undefined' &&
31293 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31294 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31296 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31299 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31303 baseScaleLevel : function()
31307 if(this.isDocument){
31309 if(this.baseRotate == 6 || this.baseRotate == 8){
31311 height = this.thumbEl.getHeight();
31312 this.baseScale = height / this.imageEl.OriginWidth;
31314 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31315 width = this.thumbEl.getWidth();
31316 this.baseScale = width / this.imageEl.OriginHeight;
31322 height = this.thumbEl.getHeight();
31323 this.baseScale = height / this.imageEl.OriginHeight;
31325 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31326 width = this.thumbEl.getWidth();
31327 this.baseScale = width / this.imageEl.OriginWidth;
31333 if(this.baseRotate == 6 || this.baseRotate == 8){
31335 width = this.thumbEl.getHeight();
31336 this.baseScale = width / this.imageEl.OriginHeight;
31338 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31339 height = this.thumbEl.getWidth();
31340 this.baseScale = height / this.imageEl.OriginHeight;
31343 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31344 height = this.thumbEl.getWidth();
31345 this.baseScale = height / this.imageEl.OriginHeight;
31347 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31348 width = this.thumbEl.getHeight();
31349 this.baseScale = width / this.imageEl.OriginWidth;
31356 width = this.thumbEl.getWidth();
31357 this.baseScale = width / this.imageEl.OriginWidth;
31359 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31360 height = this.thumbEl.getHeight();
31361 this.baseScale = height / this.imageEl.OriginHeight;
31364 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31366 height = this.thumbEl.getHeight();
31367 this.baseScale = height / this.imageEl.OriginHeight;
31369 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31370 width = this.thumbEl.getWidth();
31371 this.baseScale = width / this.imageEl.OriginWidth;
31379 getScaleLevel : function()
31381 return this.baseScale * Math.pow(1.1, this.scale);
31384 onTouchStart : function(e)
31386 if(!this.canvasLoaded){
31387 this.beforeSelectFile(e);
31391 var touches = e.browserEvent.touches;
31397 if(touches.length == 1){
31398 this.onMouseDown(e);
31402 if(touches.length != 2){
31408 for(var i = 0, finger; finger = touches[i]; i++){
31409 coords.push(finger.pageX, finger.pageY);
31412 var x = Math.pow(coords[0] - coords[2], 2);
31413 var y = Math.pow(coords[1] - coords[3], 2);
31415 this.startDistance = Math.sqrt(x + y);
31417 this.startScale = this.scale;
31419 this.pinching = true;
31420 this.dragable = false;
31424 onTouchMove : function(e)
31426 if(!this.pinching && !this.dragable){
31430 var touches = e.browserEvent.touches;
31437 this.onMouseMove(e);
31443 for(var i = 0, finger; finger = touches[i]; i++){
31444 coords.push(finger.pageX, finger.pageY);
31447 var x = Math.pow(coords[0] - coords[2], 2);
31448 var y = Math.pow(coords[1] - coords[3], 2);
31450 this.endDistance = Math.sqrt(x + y);
31452 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31454 if(!this.zoomable()){
31455 this.scale = this.startScale;
31463 onTouchEnd : function(e)
31465 this.pinching = false;
31466 this.dragable = false;
31470 process : function(file, crop)
31473 this.maskEl.mask(this.loadingText);
31476 this.xhr = new XMLHttpRequest();
31478 file.xhr = this.xhr;
31480 this.xhr.open(this.method, this.url, true);
31483 "Accept": "application/json",
31484 "Cache-Control": "no-cache",
31485 "X-Requested-With": "XMLHttpRequest"
31488 for (var headerName in headers) {
31489 var headerValue = headers[headerName];
31491 this.xhr.setRequestHeader(headerName, headerValue);
31497 this.xhr.onload = function()
31499 _this.xhrOnLoad(_this.xhr);
31502 this.xhr.onerror = function()
31504 _this.xhrOnError(_this.xhr);
31507 var formData = new FormData();
31509 formData.append('returnHTML', 'NO');
31512 formData.append('crop', crop);
31515 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31516 formData.append(this.paramName, file, file.name);
31519 if(typeof(file.filename) != 'undefined'){
31520 formData.append('filename', file.filename);
31523 if(typeof(file.mimetype) != 'undefined'){
31524 formData.append('mimetype', file.mimetype);
31527 if(this.fireEvent('arrange', this, formData) != false){
31528 this.xhr.send(formData);
31532 xhrOnLoad : function(xhr)
31535 this.maskEl.unmask();
31538 if (xhr.readyState !== 4) {
31539 this.fireEvent('exception', this, xhr);
31543 var response = Roo.decode(xhr.responseText);
31545 if(!response.success){
31546 this.fireEvent('exception', this, xhr);
31550 var response = Roo.decode(xhr.responseText);
31552 this.fireEvent('upload', this, response);
31556 xhrOnError : function()
31559 this.maskEl.unmask();
31562 Roo.log('xhr on error');
31564 var response = Roo.decode(xhr.responseText);
31570 prepare : function(file)
31573 this.maskEl.mask(this.loadingText);
31579 if(typeof(file) === 'string'){
31580 this.loadCanvas(file);
31584 if(!file || !this.urlAPI){
31589 this.cropType = file.type;
31593 if(this.fireEvent('prepare', this, this.file) != false){
31595 var reader = new FileReader();
31597 reader.onload = function (e) {
31598 if (e.target.error) {
31599 Roo.log(e.target.error);
31603 var buffer = e.target.result,
31604 dataView = new DataView(buffer),
31606 maxOffset = dataView.byteLength - 4,
31610 if (dataView.getUint16(0) === 0xffd8) {
31611 while (offset < maxOffset) {
31612 markerBytes = dataView.getUint16(offset);
31614 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31615 markerLength = dataView.getUint16(offset + 2) + 2;
31616 if (offset + markerLength > dataView.byteLength) {
31617 Roo.log('Invalid meta data: Invalid segment size.');
31621 if(markerBytes == 0xffe1){
31622 _this.parseExifData(
31629 offset += markerLength;
31639 var url = _this.urlAPI.createObjectURL(_this.file);
31641 _this.loadCanvas(url);
31646 reader.readAsArrayBuffer(this.file);
31652 parseExifData : function(dataView, offset, length)
31654 var tiffOffset = offset + 10,
31658 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31659 // No Exif data, might be XMP data instead
31663 // Check for the ASCII code for "Exif" (0x45786966):
31664 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31665 // No Exif data, might be XMP data instead
31668 if (tiffOffset + 8 > dataView.byteLength) {
31669 Roo.log('Invalid Exif data: Invalid segment size.');
31672 // Check for the two null bytes:
31673 if (dataView.getUint16(offset + 8) !== 0x0000) {
31674 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31677 // Check the byte alignment:
31678 switch (dataView.getUint16(tiffOffset)) {
31680 littleEndian = true;
31683 littleEndian = false;
31686 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31689 // Check for the TIFF tag marker (0x002A):
31690 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31691 Roo.log('Invalid Exif data: Missing TIFF marker.');
31694 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31695 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31697 this.parseExifTags(
31700 tiffOffset + dirOffset,
31705 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31710 if (dirOffset + 6 > dataView.byteLength) {
31711 Roo.log('Invalid Exif data: Invalid directory offset.');
31714 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31715 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31716 if (dirEndOffset + 4 > dataView.byteLength) {
31717 Roo.log('Invalid Exif data: Invalid directory size.');
31720 for (i = 0; i < tagsNumber; i += 1) {
31724 dirOffset + 2 + 12 * i, // tag offset
31728 // Return the offset to the next directory:
31729 return dataView.getUint32(dirEndOffset, littleEndian);
31732 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31734 var tag = dataView.getUint16(offset, littleEndian);
31736 this.exif[tag] = this.getExifValue(
31740 dataView.getUint16(offset + 2, littleEndian), // tag type
31741 dataView.getUint32(offset + 4, littleEndian), // tag length
31746 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31748 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31757 Roo.log('Invalid Exif data: Invalid tag type.');
31761 tagSize = tagType.size * length;
31762 // Determine if the value is contained in the dataOffset bytes,
31763 // or if the value at the dataOffset is a pointer to the actual data:
31764 dataOffset = tagSize > 4 ?
31765 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31766 if (dataOffset + tagSize > dataView.byteLength) {
31767 Roo.log('Invalid Exif data: Invalid data offset.');
31770 if (length === 1) {
31771 return tagType.getValue(dataView, dataOffset, littleEndian);
31774 for (i = 0; i < length; i += 1) {
31775 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31778 if (tagType.ascii) {
31780 // Concatenate the chars:
31781 for (i = 0; i < values.length; i += 1) {
31783 // Ignore the terminating NULL byte(s):
31784 if (c === '\u0000') {
31796 Roo.apply(Roo.bootstrap.UploadCropbox, {
31798 'Orientation': 0x0112
31802 1: 0, //'top-left',
31804 3: 180, //'bottom-right',
31805 // 4: 'bottom-left',
31807 6: 90, //'right-top',
31808 // 7: 'right-bottom',
31809 8: 270 //'left-bottom'
31813 // byte, 8-bit unsigned int:
31815 getValue: function (dataView, dataOffset) {
31816 return dataView.getUint8(dataOffset);
31820 // ascii, 8-bit byte:
31822 getValue: function (dataView, dataOffset) {
31823 return String.fromCharCode(dataView.getUint8(dataOffset));
31828 // short, 16 bit int:
31830 getValue: function (dataView, dataOffset, littleEndian) {
31831 return dataView.getUint16(dataOffset, littleEndian);
31835 // long, 32 bit int:
31837 getValue: function (dataView, dataOffset, littleEndian) {
31838 return dataView.getUint32(dataOffset, littleEndian);
31842 // rational = two long values, first is numerator, second is denominator:
31844 getValue: function (dataView, dataOffset, littleEndian) {
31845 return dataView.getUint32(dataOffset, littleEndian) /
31846 dataView.getUint32(dataOffset + 4, littleEndian);
31850 // slong, 32 bit signed int:
31852 getValue: function (dataView, dataOffset, littleEndian) {
31853 return dataView.getInt32(dataOffset, littleEndian);
31857 // srational, two slongs, first is numerator, second is denominator:
31859 getValue: function (dataView, dataOffset, littleEndian) {
31860 return dataView.getInt32(dataOffset, littleEndian) /
31861 dataView.getInt32(dataOffset + 4, littleEndian);
31871 cls : 'btn-group roo-upload-cropbox-rotate-left',
31872 action : 'rotate-left',
31876 cls : 'btn btn-default',
31877 html : '<i class="fa fa-undo"></i>'
31883 cls : 'btn-group roo-upload-cropbox-picture',
31884 action : 'picture',
31888 cls : 'btn btn-default',
31889 html : '<i class="fa fa-picture-o"></i>'
31895 cls : 'btn-group roo-upload-cropbox-rotate-right',
31896 action : 'rotate-right',
31900 cls : 'btn btn-default',
31901 html : '<i class="fa fa-repeat"></i>'
31909 cls : 'btn-group roo-upload-cropbox-rotate-left',
31910 action : 'rotate-left',
31914 cls : 'btn btn-default',
31915 html : '<i class="fa fa-undo"></i>'
31921 cls : 'btn-group roo-upload-cropbox-download',
31922 action : 'download',
31926 cls : 'btn btn-default',
31927 html : '<i class="fa fa-download"></i>'
31933 cls : 'btn-group roo-upload-cropbox-crop',
31938 cls : 'btn btn-default',
31939 html : '<i class="fa fa-crop"></i>'
31945 cls : 'btn-group roo-upload-cropbox-trash',
31950 cls : 'btn btn-default',
31951 html : '<i class="fa fa-trash"></i>'
31957 cls : 'btn-group roo-upload-cropbox-rotate-right',
31958 action : 'rotate-right',
31962 cls : 'btn btn-default',
31963 html : '<i class="fa fa-repeat"></i>'
31971 cls : 'btn-group roo-upload-cropbox-rotate-left',
31972 action : 'rotate-left',
31976 cls : 'btn btn-default',
31977 html : '<i class="fa fa-undo"></i>'
31983 cls : 'btn-group roo-upload-cropbox-rotate-right',
31984 action : 'rotate-right',
31988 cls : 'btn btn-default',
31989 html : '<i class="fa fa-repeat"></i>'
32002 * @class Roo.bootstrap.DocumentManager
32003 * @extends Roo.bootstrap.Component
32004 * Bootstrap DocumentManager class
32005 * @cfg {String} paramName default 'imageUpload'
32006 * @cfg {String} toolTipName default 'filename'
32007 * @cfg {String} method default POST
32008 * @cfg {String} url action url
32009 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32010 * @cfg {Boolean} multiple multiple upload default true
32011 * @cfg {Number} thumbSize default 300
32012 * @cfg {String} fieldLabel
32013 * @cfg {Number} labelWidth default 4
32014 * @cfg {String} labelAlign (left|top) default left
32015 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32016 * @cfg {Number} labellg set the width of label (1-12)
32017 * @cfg {Number} labelmd set the width of label (1-12)
32018 * @cfg {Number} labelsm set the width of label (1-12)
32019 * @cfg {Number} labelxs set the width of label (1-12)
32022 * Create a new DocumentManager
32023 * @param {Object} config The config object
32026 Roo.bootstrap.DocumentManager = function(config){
32027 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32030 this.delegates = [];
32035 * Fire when initial the DocumentManager
32036 * @param {Roo.bootstrap.DocumentManager} this
32041 * inspect selected file
32042 * @param {Roo.bootstrap.DocumentManager} this
32043 * @param {File} file
32048 * Fire when xhr load exception
32049 * @param {Roo.bootstrap.DocumentManager} this
32050 * @param {XMLHttpRequest} xhr
32052 "exception" : true,
32054 * @event afterupload
32055 * Fire when xhr load exception
32056 * @param {Roo.bootstrap.DocumentManager} this
32057 * @param {XMLHttpRequest} xhr
32059 "afterupload" : true,
32062 * prepare the form data
32063 * @param {Roo.bootstrap.DocumentManager} this
32064 * @param {Object} formData
32069 * Fire when remove the file
32070 * @param {Roo.bootstrap.DocumentManager} this
32071 * @param {Object} file
32076 * Fire after refresh the file
32077 * @param {Roo.bootstrap.DocumentManager} this
32082 * Fire after click the image
32083 * @param {Roo.bootstrap.DocumentManager} this
32084 * @param {Object} file
32089 * Fire when upload a image and editable set to true
32090 * @param {Roo.bootstrap.DocumentManager} this
32091 * @param {Object} file
32095 * @event beforeselectfile
32096 * Fire before select file
32097 * @param {Roo.bootstrap.DocumentManager} this
32099 "beforeselectfile" : true,
32102 * Fire before process file
32103 * @param {Roo.bootstrap.DocumentManager} this
32104 * @param {Object} file
32108 * @event previewrendered
32109 * Fire when preview rendered
32110 * @param {Roo.bootstrap.DocumentManager} this
32111 * @param {Object} file
32113 "previewrendered" : true,
32116 "previewResize" : true
32121 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32130 paramName : 'imageUpload',
32131 toolTipName : 'filename',
32134 labelAlign : 'left',
32144 getAutoCreate : function()
32146 var managerWidget = {
32148 cls : 'roo-document-manager',
32152 cls : 'roo-document-manager-selector',
32157 cls : 'roo-document-manager-uploader',
32161 cls : 'roo-document-manager-upload-btn',
32162 html : '<i class="fa fa-plus"></i>'
32173 cls : 'column col-md-12',
32178 if(this.fieldLabel.length){
32183 cls : 'column col-md-12',
32184 html : this.fieldLabel
32188 cls : 'column col-md-12',
32193 if(this.labelAlign == 'left'){
32198 html : this.fieldLabel
32207 if(this.labelWidth > 12){
32208 content[0].style = "width: " + this.labelWidth + 'px';
32211 if(this.labelWidth < 13 && this.labelmd == 0){
32212 this.labelmd = this.labelWidth;
32215 if(this.labellg > 0){
32216 content[0].cls += ' col-lg-' + this.labellg;
32217 content[1].cls += ' col-lg-' + (12 - this.labellg);
32220 if(this.labelmd > 0){
32221 content[0].cls += ' col-md-' + this.labelmd;
32222 content[1].cls += ' col-md-' + (12 - this.labelmd);
32225 if(this.labelsm > 0){
32226 content[0].cls += ' col-sm-' + this.labelsm;
32227 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32230 if(this.labelxs > 0){
32231 content[0].cls += ' col-xs-' + this.labelxs;
32232 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32240 cls : 'row clearfix',
32248 initEvents : function()
32250 this.managerEl = this.el.select('.roo-document-manager', true).first();
32251 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32253 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32254 this.selectorEl.hide();
32257 this.selectorEl.attr('multiple', 'multiple');
32260 this.selectorEl.on('change', this.onFileSelected, this);
32262 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32263 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32265 this.uploader.on('click', this.onUploaderClick, this);
32267 this.renderProgressDialog();
32271 window.addEventListener("resize", function() { _this.refresh(); } );
32273 this.fireEvent('initial', this);
32276 renderProgressDialog : function()
32280 this.progressDialog = new Roo.bootstrap.Modal({
32281 cls : 'roo-document-manager-progress-dialog',
32282 allow_close : false,
32293 btnclick : function() {
32294 _this.uploadCancel();
32300 this.progressDialog.render(Roo.get(document.body));
32302 this.progress = new Roo.bootstrap.Progress({
32303 cls : 'roo-document-manager-progress',
32308 this.progress.render(this.progressDialog.getChildContainer());
32310 this.progressBar = new Roo.bootstrap.ProgressBar({
32311 cls : 'roo-document-manager-progress-bar',
32314 aria_valuemax : 12,
32318 this.progressBar.render(this.progress.getChildContainer());
32321 onUploaderClick : function(e)
32323 e.preventDefault();
32325 if(this.fireEvent('beforeselectfile', this) != false){
32326 this.selectorEl.dom.click();
32331 onFileSelected : function(e)
32333 e.preventDefault();
32335 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32339 Roo.each(this.selectorEl.dom.files, function(file){
32340 if(this.fireEvent('inspect', this, file) != false){
32341 this.files.push(file);
32351 this.selectorEl.dom.value = '';
32353 if(!this.files || !this.files.length){
32357 if(this.boxes > 0 && this.files.length > this.boxes){
32358 this.files = this.files.slice(0, this.boxes);
32361 this.uploader.show();
32363 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32364 this.uploader.hide();
32373 Roo.each(this.files, function(file){
32375 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32376 var f = this.renderPreview(file);
32381 if(file.type.indexOf('image') != -1){
32382 this.delegates.push(
32384 _this.process(file);
32385 }).createDelegate(this)
32393 _this.process(file);
32394 }).createDelegate(this)
32399 this.files = files;
32401 this.delegates = this.delegates.concat(docs);
32403 if(!this.delegates.length){
32408 this.progressBar.aria_valuemax = this.delegates.length;
32415 arrange : function()
32417 if(!this.delegates.length){
32418 this.progressDialog.hide();
32423 var delegate = this.delegates.shift();
32425 this.progressDialog.show();
32427 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32429 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32434 refresh : function()
32436 this.uploader.show();
32438 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32439 this.uploader.hide();
32442 Roo.isTouch ? this.closable(false) : this.closable(true);
32444 this.fireEvent('refresh', this);
32447 onRemove : function(e, el, o)
32449 e.preventDefault();
32451 this.fireEvent('remove', this, o);
32455 remove : function(o)
32459 Roo.each(this.files, function(file){
32460 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32469 this.files = files;
32476 Roo.each(this.files, function(file){
32481 file.target.remove();
32490 onClick : function(e, el, o)
32492 e.preventDefault();
32494 this.fireEvent('click', this, o);
32498 closable : function(closable)
32500 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32502 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32514 xhrOnLoad : function(xhr)
32516 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32520 if (xhr.readyState !== 4) {
32522 this.fireEvent('exception', this, xhr);
32526 var response = Roo.decode(xhr.responseText);
32528 if(!response.success){
32530 this.fireEvent('exception', this, xhr);
32534 var file = this.renderPreview(response.data);
32536 this.files.push(file);
32540 this.fireEvent('afterupload', this, xhr);
32544 xhrOnError : function(xhr)
32546 Roo.log('xhr on error');
32548 var response = Roo.decode(xhr.responseText);
32555 process : function(file)
32557 if(this.fireEvent('process', this, file) !== false){
32558 if(this.editable && file.type.indexOf('image') != -1){
32559 this.fireEvent('edit', this, file);
32563 this.uploadStart(file, false);
32570 uploadStart : function(file, crop)
32572 this.xhr = new XMLHttpRequest();
32574 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32579 file.xhr = this.xhr;
32581 this.managerEl.createChild({
32583 cls : 'roo-document-manager-loading',
32587 tooltip : file.name,
32588 cls : 'roo-document-manager-thumb',
32589 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32595 this.xhr.open(this.method, this.url, true);
32598 "Accept": "application/json",
32599 "Cache-Control": "no-cache",
32600 "X-Requested-With": "XMLHttpRequest"
32603 for (var headerName in headers) {
32604 var headerValue = headers[headerName];
32606 this.xhr.setRequestHeader(headerName, headerValue);
32612 this.xhr.onload = function()
32614 _this.xhrOnLoad(_this.xhr);
32617 this.xhr.onerror = function()
32619 _this.xhrOnError(_this.xhr);
32622 var formData = new FormData();
32624 formData.append('returnHTML', 'NO');
32627 formData.append('crop', crop);
32630 formData.append(this.paramName, file, file.name);
32637 if(this.fireEvent('prepare', this, formData, options) != false){
32639 if(options.manually){
32643 this.xhr.send(formData);
32647 this.uploadCancel();
32650 uploadCancel : function()
32656 this.delegates = [];
32658 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32665 renderPreview : function(file)
32667 if(typeof(file.target) != 'undefined' && file.target){
32671 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32673 var previewEl = this.managerEl.createChild({
32675 cls : 'roo-document-manager-preview',
32679 tooltip : file[this.toolTipName],
32680 cls : 'roo-document-manager-thumb',
32681 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32686 html : '<i class="fa fa-times-circle"></i>'
32691 var close = previewEl.select('button.close', true).first();
32693 close.on('click', this.onRemove, this, file);
32695 file.target = previewEl;
32697 var image = previewEl.select('img', true).first();
32701 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32703 image.on('click', this.onClick, this, file);
32705 this.fireEvent('previewrendered', this, file);
32711 onPreviewLoad : function(file, image)
32713 if(typeof(file.target) == 'undefined' || !file.target){
32717 var width = image.dom.naturalWidth || image.dom.width;
32718 var height = image.dom.naturalHeight || image.dom.height;
32720 if(!this.previewResize) {
32724 if(width > height){
32725 file.target.addClass('wide');
32729 file.target.addClass('tall');
32734 uploadFromSource : function(file, crop)
32736 this.xhr = new XMLHttpRequest();
32738 this.managerEl.createChild({
32740 cls : 'roo-document-manager-loading',
32744 tooltip : file.name,
32745 cls : 'roo-document-manager-thumb',
32746 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32752 this.xhr.open(this.method, this.url, true);
32755 "Accept": "application/json",
32756 "Cache-Control": "no-cache",
32757 "X-Requested-With": "XMLHttpRequest"
32760 for (var headerName in headers) {
32761 var headerValue = headers[headerName];
32763 this.xhr.setRequestHeader(headerName, headerValue);
32769 this.xhr.onload = function()
32771 _this.xhrOnLoad(_this.xhr);
32774 this.xhr.onerror = function()
32776 _this.xhrOnError(_this.xhr);
32779 var formData = new FormData();
32781 formData.append('returnHTML', 'NO');
32783 formData.append('crop', crop);
32785 if(typeof(file.filename) != 'undefined'){
32786 formData.append('filename', file.filename);
32789 if(typeof(file.mimetype) != 'undefined'){
32790 formData.append('mimetype', file.mimetype);
32795 if(this.fireEvent('prepare', this, formData) != false){
32796 this.xhr.send(formData);
32806 * @class Roo.bootstrap.DocumentViewer
32807 * @extends Roo.bootstrap.Component
32808 * Bootstrap DocumentViewer class
32809 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32810 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32813 * Create a new DocumentViewer
32814 * @param {Object} config The config object
32817 Roo.bootstrap.DocumentViewer = function(config){
32818 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32823 * Fire after initEvent
32824 * @param {Roo.bootstrap.DocumentViewer} this
32830 * @param {Roo.bootstrap.DocumentViewer} this
32835 * Fire after download button
32836 * @param {Roo.bootstrap.DocumentViewer} this
32841 * Fire after trash button
32842 * @param {Roo.bootstrap.DocumentViewer} this
32849 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32851 showDownload : true,
32855 getAutoCreate : function()
32859 cls : 'roo-document-viewer',
32863 cls : 'roo-document-viewer-body',
32867 cls : 'roo-document-viewer-thumb',
32871 cls : 'roo-document-viewer-image'
32879 cls : 'roo-document-viewer-footer',
32882 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32886 cls : 'btn-group roo-document-viewer-download',
32890 cls : 'btn btn-default',
32891 html : '<i class="fa fa-download"></i>'
32897 cls : 'btn-group roo-document-viewer-trash',
32901 cls : 'btn btn-default',
32902 html : '<i class="fa fa-trash"></i>'
32915 initEvents : function()
32917 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32918 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32920 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32921 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32923 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32924 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32926 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32927 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32929 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32930 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32932 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32933 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32935 this.bodyEl.on('click', this.onClick, this);
32936 this.downloadBtn.on('click', this.onDownload, this);
32937 this.trashBtn.on('click', this.onTrash, this);
32939 this.downloadBtn.hide();
32940 this.trashBtn.hide();
32942 if(this.showDownload){
32943 this.downloadBtn.show();
32946 if(this.showTrash){
32947 this.trashBtn.show();
32950 if(!this.showDownload && !this.showTrash) {
32951 this.footerEl.hide();
32956 initial : function()
32958 this.fireEvent('initial', this);
32962 onClick : function(e)
32964 e.preventDefault();
32966 this.fireEvent('click', this);
32969 onDownload : function(e)
32971 e.preventDefault();
32973 this.fireEvent('download', this);
32976 onTrash : function(e)
32978 e.preventDefault();
32980 this.fireEvent('trash', this);
32992 * @class Roo.bootstrap.NavProgressBar
32993 * @extends Roo.bootstrap.Component
32994 * Bootstrap NavProgressBar class
32997 * Create a new nav progress bar
32998 * @param {Object} config The config object
33001 Roo.bootstrap.NavProgressBar = function(config){
33002 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33004 this.bullets = this.bullets || [];
33006 // Roo.bootstrap.NavProgressBar.register(this);
33010 * Fires when the active item changes
33011 * @param {Roo.bootstrap.NavProgressBar} this
33012 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33013 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33020 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33025 getAutoCreate : function()
33027 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33031 cls : 'roo-navigation-bar-group',
33035 cls : 'roo-navigation-top-bar'
33039 cls : 'roo-navigation-bullets-bar',
33043 cls : 'roo-navigation-bar'
33050 cls : 'roo-navigation-bottom-bar'
33060 initEvents: function()
33065 onRender : function(ct, position)
33067 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33069 if(this.bullets.length){
33070 Roo.each(this.bullets, function(b){
33079 addItem : function(cfg)
33081 var item = new Roo.bootstrap.NavProgressItem(cfg);
33083 item.parentId = this.id;
33084 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33087 var top = new Roo.bootstrap.Element({
33089 cls : 'roo-navigation-bar-text'
33092 var bottom = new Roo.bootstrap.Element({
33094 cls : 'roo-navigation-bar-text'
33097 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33098 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33100 var topText = new Roo.bootstrap.Element({
33102 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33105 var bottomText = new Roo.bootstrap.Element({
33107 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33110 topText.onRender(top.el, null);
33111 bottomText.onRender(bottom.el, null);
33114 item.bottomEl = bottom;
33117 this.barItems.push(item);
33122 getActive : function()
33124 var active = false;
33126 Roo.each(this.barItems, function(v){
33128 if (!v.isActive()) {
33140 setActiveItem : function(item)
33144 Roo.each(this.barItems, function(v){
33145 if (v.rid == item.rid) {
33149 if (v.isActive()) {
33150 v.setActive(false);
33155 item.setActive(true);
33157 this.fireEvent('changed', this, item, prev);
33160 getBarItem: function(rid)
33164 Roo.each(this.barItems, function(e) {
33165 if (e.rid != rid) {
33176 indexOfItem : function(item)
33180 Roo.each(this.barItems, function(v, i){
33182 if (v.rid != item.rid) {
33193 setActiveNext : function()
33195 var i = this.indexOfItem(this.getActive());
33197 if (i > this.barItems.length) {
33201 this.setActiveItem(this.barItems[i+1]);
33204 setActivePrev : function()
33206 var i = this.indexOfItem(this.getActive());
33212 this.setActiveItem(this.barItems[i-1]);
33215 format : function()
33217 if(!this.barItems.length){
33221 var width = 100 / this.barItems.length;
33223 Roo.each(this.barItems, function(i){
33224 i.el.setStyle('width', width + '%');
33225 i.topEl.el.setStyle('width', width + '%');
33226 i.bottomEl.el.setStyle('width', width + '%');
33235 * Nav Progress Item
33240 * @class Roo.bootstrap.NavProgressItem
33241 * @extends Roo.bootstrap.Component
33242 * Bootstrap NavProgressItem class
33243 * @cfg {String} rid the reference id
33244 * @cfg {Boolean} active (true|false) Is item active default false
33245 * @cfg {Boolean} disabled (true|false) Is item active default false
33246 * @cfg {String} html
33247 * @cfg {String} position (top|bottom) text position default bottom
33248 * @cfg {String} icon show icon instead of number
33251 * Create a new NavProgressItem
33252 * @param {Object} config The config object
33254 Roo.bootstrap.NavProgressItem = function(config){
33255 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33260 * The raw click event for the entire grid.
33261 * @param {Roo.bootstrap.NavProgressItem} this
33262 * @param {Roo.EventObject} e
33269 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33275 position : 'bottom',
33278 getAutoCreate : function()
33280 var iconCls = 'roo-navigation-bar-item-icon';
33282 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33286 cls: 'roo-navigation-bar-item',
33296 cfg.cls += ' active';
33299 cfg.cls += ' disabled';
33305 disable : function()
33307 this.setDisabled(true);
33310 enable : function()
33312 this.setDisabled(false);
33315 initEvents: function()
33317 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33319 this.iconEl.on('click', this.onClick, this);
33322 onClick : function(e)
33324 e.preventDefault();
33330 if(this.fireEvent('click', this, e) === false){
33334 this.parent().setActiveItem(this);
33337 isActive: function ()
33339 return this.active;
33342 setActive : function(state)
33344 if(this.active == state){
33348 this.active = state;
33351 this.el.addClass('active');
33355 this.el.removeClass('active');
33360 setDisabled : function(state)
33362 if(this.disabled == state){
33366 this.disabled = state;
33369 this.el.addClass('disabled');
33373 this.el.removeClass('disabled');
33376 tooltipEl : function()
33378 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33391 * @class Roo.bootstrap.FieldLabel
33392 * @extends Roo.bootstrap.Component
33393 * Bootstrap FieldLabel class
33394 * @cfg {String} html contents of the element
33395 * @cfg {String} tag tag of the element default label
33396 * @cfg {String} cls class of the element
33397 * @cfg {String} target label target
33398 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33399 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33400 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33401 * @cfg {String} iconTooltip default "This field is required"
33402 * @cfg {String} indicatorpos (left|right) default left
33405 * Create a new FieldLabel
33406 * @param {Object} config The config object
33409 Roo.bootstrap.FieldLabel = function(config){
33410 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33415 * Fires after the field has been marked as invalid.
33416 * @param {Roo.form.FieldLabel} this
33417 * @param {String} msg The validation message
33422 * Fires after the field has been validated with no errors.
33423 * @param {Roo.form.FieldLabel} this
33429 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33436 invalidClass : 'has-warning',
33437 validClass : 'has-success',
33438 iconTooltip : 'This field is required',
33439 indicatorpos : 'left',
33441 getAutoCreate : function(){
33444 if (!this.allowBlank) {
33450 cls : 'roo-bootstrap-field-label ' + this.cls,
33455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33456 tooltip : this.iconTooltip
33465 if(this.indicatorpos == 'right'){
33468 cls : 'roo-bootstrap-field-label ' + this.cls,
33477 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33478 tooltip : this.iconTooltip
33487 initEvents: function()
33489 Roo.bootstrap.Element.superclass.initEvents.call(this);
33491 this.indicator = this.indicatorEl();
33493 if(this.indicator){
33494 this.indicator.removeClass('visible');
33495 this.indicator.addClass('invisible');
33498 Roo.bootstrap.FieldLabel.register(this);
33501 indicatorEl : function()
33503 var indicator = this.el.select('i.roo-required-indicator',true).first();
33514 * Mark this field as valid
33516 markValid : function()
33518 if(this.indicator){
33519 this.indicator.removeClass('visible');
33520 this.indicator.addClass('invisible');
33522 if (Roo.bootstrap.version == 3) {
33523 this.el.removeClass(this.invalidClass);
33524 this.el.addClass(this.validClass);
33526 this.el.removeClass('is-invalid');
33527 this.el.addClass('is-valid');
33531 this.fireEvent('valid', this);
33535 * Mark this field as invalid
33536 * @param {String} msg The validation message
33538 markInvalid : function(msg)
33540 if(this.indicator){
33541 this.indicator.removeClass('invisible');
33542 this.indicator.addClass('visible');
33544 if (Roo.bootstrap.version == 3) {
33545 this.el.removeClass(this.validClass);
33546 this.el.addClass(this.invalidClass);
33548 this.el.removeClass('is-valid');
33549 this.el.addClass('is-invalid');
33553 this.fireEvent('invalid', this, msg);
33559 Roo.apply(Roo.bootstrap.FieldLabel, {
33564 * register a FieldLabel Group
33565 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33567 register : function(label)
33569 if(this.groups.hasOwnProperty(label.target)){
33573 this.groups[label.target] = label;
33577 * fetch a FieldLabel Group based on the target
33578 * @param {string} target
33579 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33581 get: function(target) {
33582 if (typeof(this.groups[target]) == 'undefined') {
33586 return this.groups[target] ;
33595 * page DateSplitField.
33601 * @class Roo.bootstrap.DateSplitField
33602 * @extends Roo.bootstrap.Component
33603 * Bootstrap DateSplitField class
33604 * @cfg {string} fieldLabel - the label associated
33605 * @cfg {Number} labelWidth set the width of label (0-12)
33606 * @cfg {String} labelAlign (top|left)
33607 * @cfg {Boolean} dayAllowBlank (true|false) default false
33608 * @cfg {Boolean} monthAllowBlank (true|false) default false
33609 * @cfg {Boolean} yearAllowBlank (true|false) default false
33610 * @cfg {string} dayPlaceholder
33611 * @cfg {string} monthPlaceholder
33612 * @cfg {string} yearPlaceholder
33613 * @cfg {string} dayFormat default 'd'
33614 * @cfg {string} monthFormat default 'm'
33615 * @cfg {string} yearFormat default 'Y'
33616 * @cfg {Number} labellg set the width of label (1-12)
33617 * @cfg {Number} labelmd set the width of label (1-12)
33618 * @cfg {Number} labelsm set the width of label (1-12)
33619 * @cfg {Number} labelxs set the width of label (1-12)
33623 * Create a new DateSplitField
33624 * @param {Object} config The config object
33627 Roo.bootstrap.DateSplitField = function(config){
33628 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33634 * getting the data of years
33635 * @param {Roo.bootstrap.DateSplitField} this
33636 * @param {Object} years
33641 * getting the data of days
33642 * @param {Roo.bootstrap.DateSplitField} this
33643 * @param {Object} days
33648 * Fires after the field has been marked as invalid.
33649 * @param {Roo.form.Field} this
33650 * @param {String} msg The validation message
33655 * Fires after the field has been validated with no errors.
33656 * @param {Roo.form.Field} this
33662 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33665 labelAlign : 'top',
33667 dayAllowBlank : false,
33668 monthAllowBlank : false,
33669 yearAllowBlank : false,
33670 dayPlaceholder : '',
33671 monthPlaceholder : '',
33672 yearPlaceholder : '',
33676 isFormField : true,
33682 getAutoCreate : function()
33686 cls : 'row roo-date-split-field-group',
33691 cls : 'form-hidden-field roo-date-split-field-group-value',
33697 var labelCls = 'col-md-12';
33698 var contentCls = 'col-md-4';
33700 if(this.fieldLabel){
33704 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33708 html : this.fieldLabel
33713 if(this.labelAlign == 'left'){
33715 if(this.labelWidth > 12){
33716 label.style = "width: " + this.labelWidth + 'px';
33719 if(this.labelWidth < 13 && this.labelmd == 0){
33720 this.labelmd = this.labelWidth;
33723 if(this.labellg > 0){
33724 labelCls = ' col-lg-' + this.labellg;
33725 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33728 if(this.labelmd > 0){
33729 labelCls = ' col-md-' + this.labelmd;
33730 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33733 if(this.labelsm > 0){
33734 labelCls = ' col-sm-' + this.labelsm;
33735 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33738 if(this.labelxs > 0){
33739 labelCls = ' col-xs-' + this.labelxs;
33740 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33744 label.cls += ' ' + labelCls;
33746 cfg.cn.push(label);
33749 Roo.each(['day', 'month', 'year'], function(t){
33752 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33759 inputEl: function ()
33761 return this.el.select('.roo-date-split-field-group-value', true).first();
33764 onRender : function(ct, position)
33768 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33770 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33772 this.dayField = new Roo.bootstrap.ComboBox({
33773 allowBlank : this.dayAllowBlank,
33774 alwaysQuery : true,
33775 displayField : 'value',
33778 forceSelection : true,
33780 placeholder : this.dayPlaceholder,
33781 selectOnFocus : true,
33782 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33783 triggerAction : 'all',
33785 valueField : 'value',
33786 store : new Roo.data.SimpleStore({
33787 data : (function() {
33789 _this.fireEvent('days', _this, days);
33792 fields : [ 'value' ]
33795 select : function (_self, record, index)
33797 _this.setValue(_this.getValue());
33802 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33804 this.monthField = new Roo.bootstrap.MonthField({
33805 after : '<i class=\"fa fa-calendar\"></i>',
33806 allowBlank : this.monthAllowBlank,
33807 placeholder : this.monthPlaceholder,
33810 render : function (_self)
33812 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33813 e.preventDefault();
33817 select : function (_self, oldvalue, newvalue)
33819 _this.setValue(_this.getValue());
33824 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33826 this.yearField = new Roo.bootstrap.ComboBox({
33827 allowBlank : this.yearAllowBlank,
33828 alwaysQuery : true,
33829 displayField : 'value',
33832 forceSelection : true,
33834 placeholder : this.yearPlaceholder,
33835 selectOnFocus : true,
33836 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33837 triggerAction : 'all',
33839 valueField : 'value',
33840 store : new Roo.data.SimpleStore({
33841 data : (function() {
33843 _this.fireEvent('years', _this, years);
33846 fields : [ 'value' ]
33849 select : function (_self, record, index)
33851 _this.setValue(_this.getValue());
33856 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33859 setValue : function(v, format)
33861 this.inputEl.dom.value = v;
33863 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33865 var d = Date.parseDate(v, f);
33872 this.setDay(d.format(this.dayFormat));
33873 this.setMonth(d.format(this.monthFormat));
33874 this.setYear(d.format(this.yearFormat));
33881 setDay : function(v)
33883 this.dayField.setValue(v);
33884 this.inputEl.dom.value = this.getValue();
33889 setMonth : function(v)
33891 this.monthField.setValue(v, true);
33892 this.inputEl.dom.value = this.getValue();
33897 setYear : function(v)
33899 this.yearField.setValue(v);
33900 this.inputEl.dom.value = this.getValue();
33905 getDay : function()
33907 return this.dayField.getValue();
33910 getMonth : function()
33912 return this.monthField.getValue();
33915 getYear : function()
33917 return this.yearField.getValue();
33920 getValue : function()
33922 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33924 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33934 this.inputEl.dom.value = '';
33939 validate : function()
33941 var d = this.dayField.validate();
33942 var m = this.monthField.validate();
33943 var y = this.yearField.validate();
33948 (!this.dayAllowBlank && !d) ||
33949 (!this.monthAllowBlank && !m) ||
33950 (!this.yearAllowBlank && !y)
33955 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33964 this.markInvalid();
33969 markValid : function()
33972 var label = this.el.select('label', true).first();
33973 var icon = this.el.select('i.fa-star', true).first();
33979 this.fireEvent('valid', this);
33983 * Mark this field as invalid
33984 * @param {String} msg The validation message
33986 markInvalid : function(msg)
33989 var label = this.el.select('label', true).first();
33990 var icon = this.el.select('i.fa-star', true).first();
33992 if(label && !icon){
33993 this.el.select('.roo-date-split-field-label', true).createChild({
33995 cls : 'text-danger fa fa-lg fa-star',
33996 tooltip : 'This field is required',
33997 style : 'margin-right:5px;'
34001 this.fireEvent('invalid', this, msg);
34004 clearInvalid : function()
34006 var label = this.el.select('label', true).first();
34007 var icon = this.el.select('i.fa-star', true).first();
34013 this.fireEvent('valid', this);
34016 getName: function()
34026 * http://masonry.desandro.com
34028 * The idea is to render all the bricks based on vertical width...
34030 * The original code extends 'outlayer' - we might need to use that....
34036 * @class Roo.bootstrap.LayoutMasonry
34037 * @extends Roo.bootstrap.Component
34038 * Bootstrap Layout Masonry class
34041 * Create a new Element
34042 * @param {Object} config The config object
34045 Roo.bootstrap.LayoutMasonry = function(config){
34047 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34051 Roo.bootstrap.LayoutMasonry.register(this);
34057 * Fire after layout the items
34058 * @param {Roo.bootstrap.LayoutMasonry} this
34059 * @param {Roo.EventObject} e
34066 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34069 * @cfg {Boolean} isLayoutInstant = no animation?
34071 isLayoutInstant : false, // needed?
34074 * @cfg {Number} boxWidth width of the columns
34079 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34084 * @cfg {Number} padWidth padding below box..
34089 * @cfg {Number} gutter gutter width..
34094 * @cfg {Number} maxCols maximum number of columns
34100 * @cfg {Boolean} isAutoInitial defalut true
34102 isAutoInitial : true,
34107 * @cfg {Boolean} isHorizontal defalut false
34109 isHorizontal : false,
34111 currentSize : null,
34117 bricks: null, //CompositeElement
34121 _isLayoutInited : false,
34123 // isAlternative : false, // only use for vertical layout...
34126 * @cfg {Number} alternativePadWidth padding below box..
34128 alternativePadWidth : 50,
34130 selectedBrick : [],
34132 getAutoCreate : function(){
34134 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34138 cls: 'blog-masonary-wrapper ' + this.cls,
34140 cls : 'mas-boxes masonary'
34147 getChildContainer: function( )
34149 if (this.boxesEl) {
34150 return this.boxesEl;
34153 this.boxesEl = this.el.select('.mas-boxes').first();
34155 return this.boxesEl;
34159 initEvents : function()
34163 if(this.isAutoInitial){
34164 Roo.log('hook children rendered');
34165 this.on('childrenrendered', function() {
34166 Roo.log('children rendered');
34172 initial : function()
34174 this.selectedBrick = [];
34176 this.currentSize = this.el.getBox(true);
34178 Roo.EventManager.onWindowResize(this.resize, this);
34180 if(!this.isAutoInitial){
34188 //this.layout.defer(500,this);
34192 resize : function()
34194 var cs = this.el.getBox(true);
34197 this.currentSize.width == cs.width &&
34198 this.currentSize.x == cs.x &&
34199 this.currentSize.height == cs.height &&
34200 this.currentSize.y == cs.y
34202 Roo.log("no change in with or X or Y");
34206 this.currentSize = cs;
34212 layout : function()
34214 this._resetLayout();
34216 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34218 this.layoutItems( isInstant );
34220 this._isLayoutInited = true;
34222 this.fireEvent('layout', this);
34226 _resetLayout : function()
34228 if(this.isHorizontal){
34229 this.horizontalMeasureColumns();
34233 this.verticalMeasureColumns();
34237 verticalMeasureColumns : function()
34239 this.getContainerWidth();
34241 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34242 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34246 var boxWidth = this.boxWidth + this.padWidth;
34248 if(this.containerWidth < this.boxWidth){
34249 boxWidth = this.containerWidth
34252 var containerWidth = this.containerWidth;
34254 var cols = Math.floor(containerWidth / boxWidth);
34256 this.cols = Math.max( cols, 1 );
34258 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34260 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34262 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34264 this.colWidth = boxWidth + avail - this.padWidth;
34266 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34267 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34270 horizontalMeasureColumns : function()
34272 this.getContainerWidth();
34274 var boxWidth = this.boxWidth;
34276 if(this.containerWidth < boxWidth){
34277 boxWidth = this.containerWidth;
34280 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34282 this.el.setHeight(boxWidth);
34286 getContainerWidth : function()
34288 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34291 layoutItems : function( isInstant )
34293 Roo.log(this.bricks);
34295 var items = Roo.apply([], this.bricks);
34297 if(this.isHorizontal){
34298 this._horizontalLayoutItems( items , isInstant );
34302 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34303 // this._verticalAlternativeLayoutItems( items , isInstant );
34307 this._verticalLayoutItems( items , isInstant );
34311 _verticalLayoutItems : function ( items , isInstant)
34313 if ( !items || !items.length ) {
34318 ['xs', 'xs', 'xs', 'tall'],
34319 ['xs', 'xs', 'tall'],
34320 ['xs', 'xs', 'sm'],
34321 ['xs', 'xs', 'xs'],
34327 ['sm', 'xs', 'xs'],
34331 ['tall', 'xs', 'xs', 'xs'],
34332 ['tall', 'xs', 'xs'],
34344 Roo.each(items, function(item, k){
34346 switch (item.size) {
34347 // these layouts take up a full box,
34358 boxes.push([item]);
34381 var filterPattern = function(box, length)
34389 var pattern = box.slice(0, length);
34393 Roo.each(pattern, function(i){
34394 format.push(i.size);
34397 Roo.each(standard, function(s){
34399 if(String(s) != String(format)){
34408 if(!match && length == 1){
34413 filterPattern(box, length - 1);
34417 queue.push(pattern);
34419 box = box.slice(length, box.length);
34421 filterPattern(box, 4);
34427 Roo.each(boxes, function(box, k){
34433 if(box.length == 1){
34438 filterPattern(box, 4);
34442 this._processVerticalLayoutQueue( queue, isInstant );
34446 // _verticalAlternativeLayoutItems : function( items , isInstant )
34448 // if ( !items || !items.length ) {
34452 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34456 _horizontalLayoutItems : function ( items , isInstant)
34458 if ( !items || !items.length || items.length < 3) {
34464 var eItems = items.slice(0, 3);
34466 items = items.slice(3, items.length);
34469 ['xs', 'xs', 'xs', 'wide'],
34470 ['xs', 'xs', 'wide'],
34471 ['xs', 'xs', 'sm'],
34472 ['xs', 'xs', 'xs'],
34478 ['sm', 'xs', 'xs'],
34482 ['wide', 'xs', 'xs', 'xs'],
34483 ['wide', 'xs', 'xs'],
34496 Roo.each(items, function(item, k){
34498 switch (item.size) {
34509 boxes.push([item]);
34533 var filterPattern = function(box, length)
34541 var pattern = box.slice(0, length);
34545 Roo.each(pattern, function(i){
34546 format.push(i.size);
34549 Roo.each(standard, function(s){
34551 if(String(s) != String(format)){
34560 if(!match && length == 1){
34565 filterPattern(box, length - 1);
34569 queue.push(pattern);
34571 box = box.slice(length, box.length);
34573 filterPattern(box, 4);
34579 Roo.each(boxes, function(box, k){
34585 if(box.length == 1){
34590 filterPattern(box, 4);
34597 var pos = this.el.getBox(true);
34601 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34603 var hit_end = false;
34605 Roo.each(queue, function(box){
34609 Roo.each(box, function(b){
34611 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34621 Roo.each(box, function(b){
34623 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34626 mx = Math.max(mx, b.x);
34630 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34634 Roo.each(box, function(b){
34636 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34650 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34653 /** Sets position of item in DOM
34654 * @param {Element} item
34655 * @param {Number} x - horizontal position
34656 * @param {Number} y - vertical position
34657 * @param {Boolean} isInstant - disables transitions
34659 _processVerticalLayoutQueue : function( queue, isInstant )
34661 var pos = this.el.getBox(true);
34666 for (var i = 0; i < this.cols; i++){
34670 Roo.each(queue, function(box, k){
34672 var col = k % this.cols;
34674 Roo.each(box, function(b,kk){
34676 b.el.position('absolute');
34678 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34679 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34681 if(b.size == 'md-left' || b.size == 'md-right'){
34682 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34683 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34686 b.el.setWidth(width);
34687 b.el.setHeight(height);
34689 b.el.select('iframe',true).setSize(width,height);
34693 for (var i = 0; i < this.cols; i++){
34695 if(maxY[i] < maxY[col]){
34700 col = Math.min(col, i);
34704 x = pos.x + col * (this.colWidth + this.padWidth);
34708 var positions = [];
34710 switch (box.length){
34712 positions = this.getVerticalOneBoxColPositions(x, y, box);
34715 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34718 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34721 positions = this.getVerticalFourBoxColPositions(x, y, box);
34727 Roo.each(box, function(b,kk){
34729 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34731 var sz = b.el.getSize();
34733 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34741 for (var i = 0; i < this.cols; i++){
34742 mY = Math.max(mY, maxY[i]);
34745 this.el.setHeight(mY - pos.y);
34749 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34751 // var pos = this.el.getBox(true);
34754 // var maxX = pos.right;
34756 // var maxHeight = 0;
34758 // Roo.each(items, function(item, k){
34762 // item.el.position('absolute');
34764 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34766 // item.el.setWidth(width);
34768 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34770 // item.el.setHeight(height);
34773 // item.el.setXY([x, y], isInstant ? false : true);
34775 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34778 // y = y + height + this.alternativePadWidth;
34780 // maxHeight = maxHeight + height + this.alternativePadWidth;
34784 // this.el.setHeight(maxHeight);
34788 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34790 var pos = this.el.getBox(true);
34795 var maxX = pos.right;
34797 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34799 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34801 Roo.each(queue, function(box, k){
34803 Roo.each(box, function(b, kk){
34805 b.el.position('absolute');
34807 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34808 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34810 if(b.size == 'md-left' || b.size == 'md-right'){
34811 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34812 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34815 b.el.setWidth(width);
34816 b.el.setHeight(height);
34824 var positions = [];
34826 switch (box.length){
34828 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34831 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34834 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34837 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34843 Roo.each(box, function(b,kk){
34845 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34847 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34855 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34857 Roo.each(eItems, function(b,k){
34859 b.size = (k == 0) ? 'sm' : 'xs';
34860 b.x = (k == 0) ? 2 : 1;
34861 b.y = (k == 0) ? 2 : 1;
34863 b.el.position('absolute');
34865 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34867 b.el.setWidth(width);
34869 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34871 b.el.setHeight(height);
34875 var positions = [];
34878 x : maxX - this.unitWidth * 2 - this.gutter,
34883 x : maxX - this.unitWidth,
34884 y : minY + (this.unitWidth + this.gutter) * 2
34888 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34892 Roo.each(eItems, function(b,k){
34894 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34900 getVerticalOneBoxColPositions : function(x, y, box)
34904 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34906 if(box[0].size == 'md-left'){
34910 if(box[0].size == 'md-right'){
34915 x : x + (this.unitWidth + this.gutter) * rand,
34922 getVerticalTwoBoxColPositions : function(x, y, box)
34926 if(box[0].size == 'xs'){
34930 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34934 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34948 x : x + (this.unitWidth + this.gutter) * 2,
34949 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34956 getVerticalThreeBoxColPositions : function(x, y, box)
34960 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34968 x : x + (this.unitWidth + this.gutter) * 1,
34973 x : x + (this.unitWidth + this.gutter) * 2,
34981 if(box[0].size == 'xs' && box[1].size == 'xs'){
34990 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34994 x : x + (this.unitWidth + this.gutter) * 1,
35008 x : x + (this.unitWidth + this.gutter) * 2,
35013 x : x + (this.unitWidth + this.gutter) * 2,
35014 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35021 getVerticalFourBoxColPositions : function(x, y, box)
35025 if(box[0].size == 'xs'){
35034 y : y + (this.unitHeight + this.gutter) * 1
35039 y : y + (this.unitHeight + this.gutter) * 2
35043 x : x + (this.unitWidth + this.gutter) * 1,
35057 x : x + (this.unitWidth + this.gutter) * 2,
35062 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35063 y : y + (this.unitHeight + this.gutter) * 1
35067 x : x + (this.unitWidth + this.gutter) * 2,
35068 y : y + (this.unitWidth + this.gutter) * 2
35075 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35079 if(box[0].size == 'md-left'){
35081 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35088 if(box[0].size == 'md-right'){
35090 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35091 y : minY + (this.unitWidth + this.gutter) * 1
35097 var rand = Math.floor(Math.random() * (4 - box[0].y));
35100 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35101 y : minY + (this.unitWidth + this.gutter) * rand
35108 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35112 if(box[0].size == 'xs'){
35115 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35120 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35121 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35129 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35134 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35135 y : minY + (this.unitWidth + this.gutter) * 2
35142 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35146 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35149 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35154 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35155 y : minY + (this.unitWidth + this.gutter) * 1
35159 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35160 y : minY + (this.unitWidth + this.gutter) * 2
35167 if(box[0].size == 'xs' && box[1].size == 'xs'){
35170 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35175 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35180 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35181 y : minY + (this.unitWidth + this.gutter) * 1
35189 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35194 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35195 y : minY + (this.unitWidth + this.gutter) * 2
35199 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35200 y : minY + (this.unitWidth + this.gutter) * 2
35207 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35211 if(box[0].size == 'xs'){
35214 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35219 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35224 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35229 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35230 y : minY + (this.unitWidth + this.gutter) * 1
35238 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35243 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35244 y : minY + (this.unitWidth + this.gutter) * 2
35248 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35249 y : minY + (this.unitWidth + this.gutter) * 2
35253 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35254 y : minY + (this.unitWidth + this.gutter) * 2
35262 * remove a Masonry Brick
35263 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35265 removeBrick : function(brick_id)
35271 for (var i = 0; i<this.bricks.length; i++) {
35272 if (this.bricks[i].id == brick_id) {
35273 this.bricks.splice(i,1);
35274 this.el.dom.removeChild(Roo.get(brick_id).dom);
35281 * adds a Masonry Brick
35282 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35284 addBrick : function(cfg)
35286 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35287 //this.register(cn);
35288 cn.parentId = this.id;
35289 cn.render(this.el);
35294 * register a Masonry Brick
35295 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35298 register : function(brick)
35300 this.bricks.push(brick);
35301 brick.masonryId = this.id;
35305 * clear all the Masonry Brick
35307 clearAll : function()
35310 //this.getChildContainer().dom.innerHTML = "";
35311 this.el.dom.innerHTML = '';
35314 getSelected : function()
35316 if (!this.selectedBrick) {
35320 return this.selectedBrick;
35324 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35328 * register a Masonry Layout
35329 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35332 register : function(layout)
35334 this.groups[layout.id] = layout;
35337 * fetch a Masonry Layout based on the masonry layout ID
35338 * @param {string} the masonry layout to add
35339 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35342 get: function(layout_id) {
35343 if (typeof(this.groups[layout_id]) == 'undefined') {
35346 return this.groups[layout_id] ;
35358 * http://masonry.desandro.com
35360 * The idea is to render all the bricks based on vertical width...
35362 * The original code extends 'outlayer' - we might need to use that....
35368 * @class Roo.bootstrap.LayoutMasonryAuto
35369 * @extends Roo.bootstrap.Component
35370 * Bootstrap Layout Masonry class
35373 * Create a new Element
35374 * @param {Object} config The config object
35377 Roo.bootstrap.LayoutMasonryAuto = function(config){
35378 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35381 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35384 * @cfg {Boolean} isFitWidth - resize the width..
35386 isFitWidth : false, // options..
35388 * @cfg {Boolean} isOriginLeft = left align?
35390 isOriginLeft : true,
35392 * @cfg {Boolean} isOriginTop = top align?
35394 isOriginTop : false,
35396 * @cfg {Boolean} isLayoutInstant = no animation?
35398 isLayoutInstant : false, // needed?
35400 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35402 isResizingContainer : true,
35404 * @cfg {Number} columnWidth width of the columns
35410 * @cfg {Number} maxCols maximum number of columns
35415 * @cfg {Number} padHeight padding below box..
35421 * @cfg {Boolean} isAutoInitial defalut true
35424 isAutoInitial : true,
35430 initialColumnWidth : 0,
35431 currentSize : null,
35433 colYs : null, // array.
35440 bricks: null, //CompositeElement
35441 cols : 0, // array?
35442 // element : null, // wrapped now this.el
35443 _isLayoutInited : null,
35446 getAutoCreate : function(){
35450 cls: 'blog-masonary-wrapper ' + this.cls,
35452 cls : 'mas-boxes masonary'
35459 getChildContainer: function( )
35461 if (this.boxesEl) {
35462 return this.boxesEl;
35465 this.boxesEl = this.el.select('.mas-boxes').first();
35467 return this.boxesEl;
35471 initEvents : function()
35475 if(this.isAutoInitial){
35476 Roo.log('hook children rendered');
35477 this.on('childrenrendered', function() {
35478 Roo.log('children rendered');
35485 initial : function()
35487 this.reloadItems();
35489 this.currentSize = this.el.getBox(true);
35491 /// was window resize... - let's see if this works..
35492 Roo.EventManager.onWindowResize(this.resize, this);
35494 if(!this.isAutoInitial){
35499 this.layout.defer(500,this);
35502 reloadItems: function()
35504 this.bricks = this.el.select('.masonry-brick', true);
35506 this.bricks.each(function(b) {
35507 //Roo.log(b.getSize());
35508 if (!b.attr('originalwidth')) {
35509 b.attr('originalwidth', b.getSize().width);
35514 Roo.log(this.bricks.elements.length);
35517 resize : function()
35520 var cs = this.el.getBox(true);
35522 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35523 Roo.log("no change in with or X");
35526 this.currentSize = cs;
35530 layout : function()
35533 this._resetLayout();
35534 //this._manageStamps();
35536 // don't animate first layout
35537 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35538 this.layoutItems( isInstant );
35540 // flag for initalized
35541 this._isLayoutInited = true;
35544 layoutItems : function( isInstant )
35546 //var items = this._getItemsForLayout( this.items );
35547 // original code supports filtering layout items.. we just ignore it..
35549 this._layoutItems( this.bricks , isInstant );
35551 this._postLayout();
35553 _layoutItems : function ( items , isInstant)
35555 //this.fireEvent( 'layout', this, items );
35558 if ( !items || !items.elements.length ) {
35559 // no items, emit event with empty array
35564 items.each(function(item) {
35565 Roo.log("layout item");
35567 // get x/y object from method
35568 var position = this._getItemLayoutPosition( item );
35570 position.item = item;
35571 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35572 queue.push( position );
35575 this._processLayoutQueue( queue );
35577 /** Sets position of item in DOM
35578 * @param {Element} item
35579 * @param {Number} x - horizontal position
35580 * @param {Number} y - vertical position
35581 * @param {Boolean} isInstant - disables transitions
35583 _processLayoutQueue : function( queue )
35585 for ( var i=0, len = queue.length; i < len; i++ ) {
35586 var obj = queue[i];
35587 obj.item.position('absolute');
35588 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35594 * Any logic you want to do after each layout,
35595 * i.e. size the container
35597 _postLayout : function()
35599 this.resizeContainer();
35602 resizeContainer : function()
35604 if ( !this.isResizingContainer ) {
35607 var size = this._getContainerSize();
35609 this.el.setSize(size.width,size.height);
35610 this.boxesEl.setSize(size.width,size.height);
35616 _resetLayout : function()
35618 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35619 this.colWidth = this.el.getWidth();
35620 //this.gutter = this.el.getWidth();
35622 this.measureColumns();
35628 this.colYs.push( 0 );
35634 measureColumns : function()
35636 this.getContainerWidth();
35637 // if columnWidth is 0, default to outerWidth of first item
35638 if ( !this.columnWidth ) {
35639 var firstItem = this.bricks.first();
35640 Roo.log(firstItem);
35641 this.columnWidth = this.containerWidth;
35642 if (firstItem && firstItem.attr('originalwidth') ) {
35643 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35645 // columnWidth fall back to item of first element
35646 Roo.log("set column width?");
35647 this.initialColumnWidth = this.columnWidth ;
35649 // if first elem has no width, default to size of container
35654 if (this.initialColumnWidth) {
35655 this.columnWidth = this.initialColumnWidth;
35660 // column width is fixed at the top - however if container width get's smaller we should
35663 // this bit calcs how man columns..
35665 var columnWidth = this.columnWidth += this.gutter;
35667 // calculate columns
35668 var containerWidth = this.containerWidth + this.gutter;
35670 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35671 // fix rounding errors, typically with gutters
35672 var excess = columnWidth - containerWidth % columnWidth;
35675 // if overshoot is less than a pixel, round up, otherwise floor it
35676 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35677 cols = Math[ mathMethod ]( cols );
35678 this.cols = Math.max( cols, 1 );
35679 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35681 // padding positioning..
35682 var totalColWidth = this.cols * this.columnWidth;
35683 var padavail = this.containerWidth - totalColWidth;
35684 // so for 2 columns - we need 3 'pads'
35686 var padNeeded = (1+this.cols) * this.padWidth;
35688 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35690 this.columnWidth += padExtra
35691 //this.padWidth = Math.floor(padavail / ( this.cols));
35693 // adjust colum width so that padding is fixed??
35695 // we have 3 columns ... total = width * 3
35696 // we have X left over... that should be used by
35698 //if (this.expandC) {
35706 getContainerWidth : function()
35708 /* // container is parent if fit width
35709 var container = this.isFitWidth ? this.element.parentNode : this.element;
35710 // check that this.size and size are there
35711 // IE8 triggers resize on body size change, so they might not be
35713 var size = getSize( container ); //FIXME
35714 this.containerWidth = size && size.innerWidth; //FIXME
35717 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35721 _getItemLayoutPosition : function( item ) // what is item?
35723 // we resize the item to our columnWidth..
35725 item.setWidth(this.columnWidth);
35726 item.autoBoxAdjust = false;
35728 var sz = item.getSize();
35730 // how many columns does this brick span
35731 var remainder = this.containerWidth % this.columnWidth;
35733 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35734 // round if off by 1 pixel, otherwise use ceil
35735 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35736 colSpan = Math.min( colSpan, this.cols );
35738 // normally this should be '1' as we dont' currently allow multi width columns..
35740 var colGroup = this._getColGroup( colSpan );
35741 // get the minimum Y value from the columns
35742 var minimumY = Math.min.apply( Math, colGroup );
35743 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35745 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35747 // position the brick
35749 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35750 y: this.currentSize.y + minimumY + this.padHeight
35754 // apply setHeight to necessary columns
35755 var setHeight = minimumY + sz.height + this.padHeight;
35756 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35758 var setSpan = this.cols + 1 - colGroup.length;
35759 for ( var i = 0; i < setSpan; i++ ) {
35760 this.colYs[ shortColIndex + i ] = setHeight ;
35767 * @param {Number} colSpan - number of columns the element spans
35768 * @returns {Array} colGroup
35770 _getColGroup : function( colSpan )
35772 if ( colSpan < 2 ) {
35773 // if brick spans only one column, use all the column Ys
35778 // how many different places could this brick fit horizontally
35779 var groupCount = this.cols + 1 - colSpan;
35780 // for each group potential horizontal position
35781 for ( var i = 0; i < groupCount; i++ ) {
35782 // make an array of colY values for that one group
35783 var groupColYs = this.colYs.slice( i, i + colSpan );
35784 // and get the max value of the array
35785 colGroup[i] = Math.max.apply( Math, groupColYs );
35790 _manageStamp : function( stamp )
35792 var stampSize = stamp.getSize();
35793 var offset = stamp.getBox();
35794 // get the columns that this stamp affects
35795 var firstX = this.isOriginLeft ? offset.x : offset.right;
35796 var lastX = firstX + stampSize.width;
35797 var firstCol = Math.floor( firstX / this.columnWidth );
35798 firstCol = Math.max( 0, firstCol );
35800 var lastCol = Math.floor( lastX / this.columnWidth );
35801 // lastCol should not go over if multiple of columnWidth #425
35802 lastCol -= lastX % this.columnWidth ? 0 : 1;
35803 lastCol = Math.min( this.cols - 1, lastCol );
35805 // set colYs to bottom of the stamp
35806 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35809 for ( var i = firstCol; i <= lastCol; i++ ) {
35810 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35815 _getContainerSize : function()
35817 this.maxY = Math.max.apply( Math, this.colYs );
35822 if ( this.isFitWidth ) {
35823 size.width = this._getContainerFitWidth();
35829 _getContainerFitWidth : function()
35831 var unusedCols = 0;
35832 // count unused columns
35835 if ( this.colYs[i] !== 0 ) {
35840 // fit container to columns that have been used
35841 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35844 needsResizeLayout : function()
35846 var previousWidth = this.containerWidth;
35847 this.getContainerWidth();
35848 return previousWidth !== this.containerWidth;
35863 * @class Roo.bootstrap.MasonryBrick
35864 * @extends Roo.bootstrap.Component
35865 * Bootstrap MasonryBrick class
35868 * Create a new MasonryBrick
35869 * @param {Object} config The config object
35872 Roo.bootstrap.MasonryBrick = function(config){
35874 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35876 Roo.bootstrap.MasonryBrick.register(this);
35882 * When a MasonryBrick is clcik
35883 * @param {Roo.bootstrap.MasonryBrick} this
35884 * @param {Roo.EventObject} e
35890 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35893 * @cfg {String} title
35897 * @cfg {String} html
35901 * @cfg {String} bgimage
35905 * @cfg {String} videourl
35909 * @cfg {String} cls
35913 * @cfg {String} href
35917 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35922 * @cfg {String} placetitle (center|bottom)
35927 * @cfg {Boolean} isFitContainer defalut true
35929 isFitContainer : true,
35932 * @cfg {Boolean} preventDefault defalut false
35934 preventDefault : false,
35937 * @cfg {Boolean} inverse defalut false
35939 maskInverse : false,
35941 getAutoCreate : function()
35943 if(!this.isFitContainer){
35944 return this.getSplitAutoCreate();
35947 var cls = 'masonry-brick masonry-brick-full';
35949 if(this.href.length){
35950 cls += ' masonry-brick-link';
35953 if(this.bgimage.length){
35954 cls += ' masonry-brick-image';
35957 if(this.maskInverse){
35958 cls += ' mask-inverse';
35961 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35962 cls += ' enable-mask';
35966 cls += ' masonry-' + this.size + '-brick';
35969 if(this.placetitle.length){
35971 switch (this.placetitle) {
35973 cls += ' masonry-center-title';
35976 cls += ' masonry-bottom-title';
35983 if(!this.html.length && !this.bgimage.length){
35984 cls += ' masonry-center-title';
35987 if(!this.html.length && this.bgimage.length){
35988 cls += ' masonry-bottom-title';
35993 cls += ' ' + this.cls;
35997 tag: (this.href.length) ? 'a' : 'div',
36002 cls: 'masonry-brick-mask'
36006 cls: 'masonry-brick-paragraph',
36012 if(this.href.length){
36013 cfg.href = this.href;
36016 var cn = cfg.cn[1].cn;
36018 if(this.title.length){
36021 cls: 'masonry-brick-title',
36026 if(this.html.length){
36029 cls: 'masonry-brick-text',
36034 if (!this.title.length && !this.html.length) {
36035 cfg.cn[1].cls += ' hide';
36038 if(this.bgimage.length){
36041 cls: 'masonry-brick-image-view',
36046 if(this.videourl.length){
36047 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36048 // youtube support only?
36051 cls: 'masonry-brick-image-view',
36054 allowfullscreen : true
36062 getSplitAutoCreate : function()
36064 var cls = 'masonry-brick masonry-brick-split';
36066 if(this.href.length){
36067 cls += ' masonry-brick-link';
36070 if(this.bgimage.length){
36071 cls += ' masonry-brick-image';
36075 cls += ' masonry-' + this.size + '-brick';
36078 switch (this.placetitle) {
36080 cls += ' masonry-center-title';
36083 cls += ' masonry-bottom-title';
36086 if(!this.bgimage.length){
36087 cls += ' masonry-center-title';
36090 if(this.bgimage.length){
36091 cls += ' masonry-bottom-title';
36097 cls += ' ' + this.cls;
36101 tag: (this.href.length) ? 'a' : 'div',
36106 cls: 'masonry-brick-split-head',
36110 cls: 'masonry-brick-paragraph',
36117 cls: 'masonry-brick-split-body',
36123 if(this.href.length){
36124 cfg.href = this.href;
36127 if(this.title.length){
36128 cfg.cn[0].cn[0].cn.push({
36130 cls: 'masonry-brick-title',
36135 if(this.html.length){
36136 cfg.cn[1].cn.push({
36138 cls: 'masonry-brick-text',
36143 if(this.bgimage.length){
36144 cfg.cn[0].cn.push({
36146 cls: 'masonry-brick-image-view',
36151 if(this.videourl.length){
36152 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36153 // youtube support only?
36154 cfg.cn[0].cn.cn.push({
36156 cls: 'masonry-brick-image-view',
36159 allowfullscreen : true
36166 initEvents: function()
36168 switch (this.size) {
36201 this.el.on('touchstart', this.onTouchStart, this);
36202 this.el.on('touchmove', this.onTouchMove, this);
36203 this.el.on('touchend', this.onTouchEnd, this);
36204 this.el.on('contextmenu', this.onContextMenu, this);
36206 this.el.on('mouseenter' ,this.enter, this);
36207 this.el.on('mouseleave', this.leave, this);
36208 this.el.on('click', this.onClick, this);
36211 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36212 this.parent().bricks.push(this);
36217 onClick: function(e, el)
36219 var time = this.endTimer - this.startTimer;
36220 // Roo.log(e.preventDefault());
36223 e.preventDefault();
36228 if(!this.preventDefault){
36232 e.preventDefault();
36234 if (this.activeClass != '') {
36235 this.selectBrick();
36238 this.fireEvent('click', this, e);
36241 enter: function(e, el)
36243 e.preventDefault();
36245 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36249 if(this.bgimage.length && this.html.length){
36250 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36254 leave: function(e, el)
36256 e.preventDefault();
36258 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36262 if(this.bgimage.length && this.html.length){
36263 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36267 onTouchStart: function(e, el)
36269 // e.preventDefault();
36271 this.touchmoved = false;
36273 if(!this.isFitContainer){
36277 if(!this.bgimage.length || !this.html.length){
36281 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36283 this.timer = new Date().getTime();
36287 onTouchMove: function(e, el)
36289 this.touchmoved = true;
36292 onContextMenu : function(e,el)
36294 e.preventDefault();
36295 e.stopPropagation();
36299 onTouchEnd: function(e, el)
36301 // e.preventDefault();
36303 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36310 if(!this.bgimage.length || !this.html.length){
36312 if(this.href.length){
36313 window.location.href = this.href;
36319 if(!this.isFitContainer){
36323 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36325 window.location.href = this.href;
36328 //selection on single brick only
36329 selectBrick : function() {
36331 if (!this.parentId) {
36335 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36336 var index = m.selectedBrick.indexOf(this.id);
36339 m.selectedBrick.splice(index,1);
36340 this.el.removeClass(this.activeClass);
36344 for(var i = 0; i < m.selectedBrick.length; i++) {
36345 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36346 b.el.removeClass(b.activeClass);
36349 m.selectedBrick = [];
36351 m.selectedBrick.push(this.id);
36352 this.el.addClass(this.activeClass);
36356 isSelected : function(){
36357 return this.el.hasClass(this.activeClass);
36362 Roo.apply(Roo.bootstrap.MasonryBrick, {
36365 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36367 * register a Masonry Brick
36368 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36371 register : function(brick)
36373 //this.groups[brick.id] = brick;
36374 this.groups.add(brick.id, brick);
36377 * fetch a masonry brick based on the masonry brick ID
36378 * @param {string} the masonry brick to add
36379 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36382 get: function(brick_id)
36384 // if (typeof(this.groups[brick_id]) == 'undefined') {
36387 // return this.groups[brick_id] ;
36389 if(this.groups.key(brick_id)) {
36390 return this.groups.key(brick_id);
36408 * @class Roo.bootstrap.Brick
36409 * @extends Roo.bootstrap.Component
36410 * Bootstrap Brick class
36413 * Create a new Brick
36414 * @param {Object} config The config object
36417 Roo.bootstrap.Brick = function(config){
36418 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36424 * When a Brick is click
36425 * @param {Roo.bootstrap.Brick} this
36426 * @param {Roo.EventObject} e
36432 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36435 * @cfg {String} title
36439 * @cfg {String} html
36443 * @cfg {String} bgimage
36447 * @cfg {String} cls
36451 * @cfg {String} href
36455 * @cfg {String} video
36459 * @cfg {Boolean} square
36463 getAutoCreate : function()
36465 var cls = 'roo-brick';
36467 if(this.href.length){
36468 cls += ' roo-brick-link';
36471 if(this.bgimage.length){
36472 cls += ' roo-brick-image';
36475 if(!this.html.length && !this.bgimage.length){
36476 cls += ' roo-brick-center-title';
36479 if(!this.html.length && this.bgimage.length){
36480 cls += ' roo-brick-bottom-title';
36484 cls += ' ' + this.cls;
36488 tag: (this.href.length) ? 'a' : 'div',
36493 cls: 'roo-brick-paragraph',
36499 if(this.href.length){
36500 cfg.href = this.href;
36503 var cn = cfg.cn[0].cn;
36505 if(this.title.length){
36508 cls: 'roo-brick-title',
36513 if(this.html.length){
36516 cls: 'roo-brick-text',
36523 if(this.bgimage.length){
36526 cls: 'roo-brick-image-view',
36534 initEvents: function()
36536 if(this.title.length || this.html.length){
36537 this.el.on('mouseenter' ,this.enter, this);
36538 this.el.on('mouseleave', this.leave, this);
36541 Roo.EventManager.onWindowResize(this.resize, this);
36543 if(this.bgimage.length){
36544 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36545 this.imageEl.on('load', this.onImageLoad, this);
36552 onImageLoad : function()
36557 resize : function()
36559 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36561 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36563 if(this.bgimage.length){
36564 var image = this.el.select('.roo-brick-image-view', true).first();
36566 image.setWidth(paragraph.getWidth());
36569 image.setHeight(paragraph.getWidth());
36572 this.el.setHeight(image.getHeight());
36573 paragraph.setHeight(image.getHeight());
36579 enter: function(e, el)
36581 e.preventDefault();
36583 if(this.bgimage.length){
36584 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36585 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36589 leave: function(e, el)
36591 e.preventDefault();
36593 if(this.bgimage.length){
36594 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36595 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36610 * @class Roo.bootstrap.NumberField
36611 * @extends Roo.bootstrap.Input
36612 * Bootstrap NumberField class
36618 * Create a new NumberField
36619 * @param {Object} config The config object
36622 Roo.bootstrap.NumberField = function(config){
36623 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36626 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36629 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36631 allowDecimals : true,
36633 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36635 decimalSeparator : ".",
36637 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36639 decimalPrecision : 2,
36641 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36643 allowNegative : true,
36646 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36650 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36652 minValue : Number.NEGATIVE_INFINITY,
36654 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36656 maxValue : Number.MAX_VALUE,
36658 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36660 minText : "The minimum value for this field is {0}",
36662 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36664 maxText : "The maximum value for this field is {0}",
36666 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36667 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36669 nanText : "{0} is not a valid number",
36671 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36673 thousandsDelimiter : false,
36675 * @cfg {String} valueAlign alignment of value
36677 valueAlign : "left",
36679 getAutoCreate : function()
36681 var hiddenInput = {
36685 cls: 'hidden-number-input'
36689 hiddenInput.name = this.name;
36694 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36696 this.name = hiddenInput.name;
36698 if(cfg.cn.length > 0) {
36699 cfg.cn.push(hiddenInput);
36706 initEvents : function()
36708 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36710 var allowed = "0123456789";
36712 if(this.allowDecimals){
36713 allowed += this.decimalSeparator;
36716 if(this.allowNegative){
36720 if(this.thousandsDelimiter) {
36724 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36726 var keyPress = function(e){
36728 var k = e.getKey();
36730 var c = e.getCharCode();
36733 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36734 allowed.indexOf(String.fromCharCode(c)) === -1
36740 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36744 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36749 this.el.on("keypress", keyPress, this);
36752 validateValue : function(value)
36755 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36759 var num = this.parseValue(value);
36762 this.markInvalid(String.format(this.nanText, value));
36766 if(num < this.minValue){
36767 this.markInvalid(String.format(this.minText, this.minValue));
36771 if(num > this.maxValue){
36772 this.markInvalid(String.format(this.maxText, this.maxValue));
36779 getValue : function()
36781 var v = this.hiddenEl().getValue();
36783 return this.fixPrecision(this.parseValue(v));
36786 parseValue : function(value)
36788 if(this.thousandsDelimiter) {
36790 r = new RegExp(",", "g");
36791 value = value.replace(r, "");
36794 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36795 return isNaN(value) ? '' : value;
36798 fixPrecision : function(value)
36800 if(this.thousandsDelimiter) {
36802 r = new RegExp(",", "g");
36803 value = value.replace(r, "");
36806 var nan = isNaN(value);
36808 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36809 return nan ? '' : value;
36811 return parseFloat(value).toFixed(this.decimalPrecision);
36814 setValue : function(v)
36816 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36822 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36824 this.inputEl().dom.value = (v == '') ? '' :
36825 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36827 if(!this.allowZero && v === '0') {
36828 this.hiddenEl().dom.value = '';
36829 this.inputEl().dom.value = '';
36836 decimalPrecisionFcn : function(v)
36838 return Math.floor(v);
36841 beforeBlur : function()
36843 var v = this.parseValue(this.getRawValue());
36845 if(v || v === 0 || v === ''){
36850 hiddenEl : function()
36852 return this.el.select('input.hidden-number-input',true).first();
36864 * @class Roo.bootstrap.DocumentSlider
36865 * @extends Roo.bootstrap.Component
36866 * Bootstrap DocumentSlider class
36869 * Create a new DocumentViewer
36870 * @param {Object} config The config object
36873 Roo.bootstrap.DocumentSlider = function(config){
36874 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36881 * Fire after initEvent
36882 * @param {Roo.bootstrap.DocumentSlider} this
36887 * Fire after update
36888 * @param {Roo.bootstrap.DocumentSlider} this
36894 * @param {Roo.bootstrap.DocumentSlider} this
36900 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36906 getAutoCreate : function()
36910 cls : 'roo-document-slider',
36914 cls : 'roo-document-slider-header',
36918 cls : 'roo-document-slider-header-title'
36924 cls : 'roo-document-slider-body',
36928 cls : 'roo-document-slider-prev',
36932 cls : 'fa fa-chevron-left'
36938 cls : 'roo-document-slider-thumb',
36942 cls : 'roo-document-slider-image'
36948 cls : 'roo-document-slider-next',
36952 cls : 'fa fa-chevron-right'
36964 initEvents : function()
36966 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36967 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36969 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36970 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36972 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36973 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36975 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36976 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36978 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36979 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36981 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36982 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36984 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36985 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36987 this.thumbEl.on('click', this.onClick, this);
36989 this.prevIndicator.on('click', this.prev, this);
36991 this.nextIndicator.on('click', this.next, this);
36995 initial : function()
36997 if(this.files.length){
36998 this.indicator = 1;
37002 this.fireEvent('initial', this);
37005 update : function()
37007 this.imageEl.attr('src', this.files[this.indicator - 1]);
37009 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37011 this.prevIndicator.show();
37013 if(this.indicator == 1){
37014 this.prevIndicator.hide();
37017 this.nextIndicator.show();
37019 if(this.indicator == this.files.length){
37020 this.nextIndicator.hide();
37023 this.thumbEl.scrollTo('top');
37025 this.fireEvent('update', this);
37028 onClick : function(e)
37030 e.preventDefault();
37032 this.fireEvent('click', this);
37037 e.preventDefault();
37039 this.indicator = Math.max(1, this.indicator - 1);
37046 e.preventDefault();
37048 this.indicator = Math.min(this.files.length, this.indicator + 1);
37062 * @class Roo.bootstrap.RadioSet
37063 * @extends Roo.bootstrap.Input
37064 * Bootstrap RadioSet class
37065 * @cfg {String} indicatorpos (left|right) default left
37066 * @cfg {Boolean} inline (true|false) inline the element (default true)
37067 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37069 * Create a new RadioSet
37070 * @param {Object} config The config object
37073 Roo.bootstrap.RadioSet = function(config){
37075 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37079 Roo.bootstrap.RadioSet.register(this);
37084 * Fires when the element is checked or unchecked.
37085 * @param {Roo.bootstrap.RadioSet} this This radio
37086 * @param {Roo.bootstrap.Radio} item The checked item
37091 * Fires when the element is click.
37092 * @param {Roo.bootstrap.RadioSet} this This radio set
37093 * @param {Roo.bootstrap.Radio} item The checked item
37094 * @param {Roo.EventObject} e The event object
37101 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37109 indicatorpos : 'left',
37111 getAutoCreate : function()
37115 cls : 'roo-radio-set-label',
37119 html : this.fieldLabel
37123 if (Roo.bootstrap.version == 3) {
37126 if(this.indicatorpos == 'left'){
37129 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37130 tooltip : 'This field is required'
37135 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37136 tooltip : 'This field is required'
37142 cls : 'roo-radio-set-items'
37145 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37147 if (align === 'left' && this.fieldLabel.length) {
37150 cls : "roo-radio-set-right",
37156 if(this.labelWidth > 12){
37157 label.style = "width: " + this.labelWidth + 'px';
37160 if(this.labelWidth < 13 && this.labelmd == 0){
37161 this.labelmd = this.labelWidth;
37164 if(this.labellg > 0){
37165 label.cls += ' col-lg-' + this.labellg;
37166 items.cls += ' col-lg-' + (12 - this.labellg);
37169 if(this.labelmd > 0){
37170 label.cls += ' col-md-' + this.labelmd;
37171 items.cls += ' col-md-' + (12 - this.labelmd);
37174 if(this.labelsm > 0){
37175 label.cls += ' col-sm-' + this.labelsm;
37176 items.cls += ' col-sm-' + (12 - this.labelsm);
37179 if(this.labelxs > 0){
37180 label.cls += ' col-xs-' + this.labelxs;
37181 items.cls += ' col-xs-' + (12 - this.labelxs);
37187 cls : 'roo-radio-set',
37191 cls : 'roo-radio-set-input',
37194 value : this.value ? this.value : ''
37201 if(this.weight.length){
37202 cfg.cls += ' roo-radio-' + this.weight;
37206 cfg.cls += ' roo-radio-set-inline';
37210 ['xs','sm','md','lg'].map(function(size){
37211 if (settings[size]) {
37212 cfg.cls += ' col-' + size + '-' + settings[size];
37220 initEvents : function()
37222 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37223 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37225 if(!this.fieldLabel.length){
37226 this.labelEl.hide();
37229 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37230 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37232 this.indicator = this.indicatorEl();
37234 if(this.indicator){
37235 this.indicator.addClass('invisible');
37238 this.originalValue = this.getValue();
37242 inputEl: function ()
37244 return this.el.select('.roo-radio-set-input', true).first();
37247 getChildContainer : function()
37249 return this.itemsEl;
37252 register : function(item)
37254 this.radioes.push(item);
37258 validate : function()
37260 if(this.getVisibilityEl().hasClass('hidden')){
37266 Roo.each(this.radioes, function(i){
37275 if(this.allowBlank) {
37279 if(this.disabled || valid){
37284 this.markInvalid();
37289 markValid : function()
37291 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37292 this.indicatorEl().removeClass('visible');
37293 this.indicatorEl().addClass('invisible');
37297 if (Roo.bootstrap.version == 3) {
37298 this.el.removeClass([this.invalidClass, this.validClass]);
37299 this.el.addClass(this.validClass);
37301 this.el.removeClass(['is-invalid','is-valid']);
37302 this.el.addClass(['is-valid']);
37304 this.fireEvent('valid', this);
37307 markInvalid : function(msg)
37309 if(this.allowBlank || this.disabled){
37313 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37314 this.indicatorEl().removeClass('invisible');
37315 this.indicatorEl().addClass('visible');
37317 if (Roo.bootstrap.version == 3) {
37318 this.el.removeClass([this.invalidClass, this.validClass]);
37319 this.el.addClass(this.invalidClass);
37321 this.el.removeClass(['is-invalid','is-valid']);
37322 this.el.addClass(['is-invalid']);
37325 this.fireEvent('invalid', this, msg);
37329 setValue : function(v, suppressEvent)
37331 if(this.value === v){
37338 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37341 Roo.each(this.radioes, function(i){
37343 i.el.removeClass('checked');
37346 Roo.each(this.radioes, function(i){
37348 if(i.value === v || i.value.toString() === v.toString()){
37350 i.el.addClass('checked');
37352 if(suppressEvent !== true){
37353 this.fireEvent('check', this, i);
37364 clearInvalid : function(){
37366 if(!this.el || this.preventMark){
37370 this.el.removeClass([this.invalidClass]);
37372 this.fireEvent('valid', this);
37377 Roo.apply(Roo.bootstrap.RadioSet, {
37381 register : function(set)
37383 this.groups[set.name] = set;
37386 get: function(name)
37388 if (typeof(this.groups[name]) == 'undefined') {
37392 return this.groups[name] ;
37398 * Ext JS Library 1.1.1
37399 * Copyright(c) 2006-2007, Ext JS, LLC.
37401 * Originally Released Under LGPL - original licence link has changed is not relivant.
37404 * <script type="text/javascript">
37409 * @class Roo.bootstrap.SplitBar
37410 * @extends Roo.util.Observable
37411 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37415 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37416 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37417 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37418 split.minSize = 100;
37419 split.maxSize = 600;
37420 split.animate = true;
37421 split.on('moved', splitterMoved);
37424 * Create a new SplitBar
37425 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37426 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37427 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37428 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37429 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37430 position of the SplitBar).
37432 Roo.bootstrap.SplitBar = function(cfg){
37437 // dragElement : elm
37438 // resizingElement: el,
37440 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37441 // placement : Roo.bootstrap.SplitBar.LEFT ,
37442 // existingProxy ???
37445 this.el = Roo.get(cfg.dragElement, true);
37446 this.el.dom.unselectable = "on";
37448 this.resizingEl = Roo.get(cfg.resizingElement, true);
37452 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37453 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37456 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37459 * The minimum size of the resizing element. (Defaults to 0)
37465 * The maximum size of the resizing element. (Defaults to 2000)
37468 this.maxSize = 2000;
37471 * Whether to animate the transition to the new size
37474 this.animate = false;
37477 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37480 this.useShim = false;
37485 if(!cfg.existingProxy){
37487 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37489 this.proxy = Roo.get(cfg.existingProxy).dom;
37492 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37495 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37498 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37501 this.dragSpecs = {};
37504 * @private The adapter to use to positon and resize elements
37506 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37507 this.adapter.init(this);
37509 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37511 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37512 this.el.addClass("roo-splitbar-h");
37515 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37516 this.el.addClass("roo-splitbar-v");
37522 * Fires when the splitter is moved (alias for {@link #event-moved})
37523 * @param {Roo.bootstrap.SplitBar} this
37524 * @param {Number} newSize the new width or height
37529 * Fires when the splitter is moved
37530 * @param {Roo.bootstrap.SplitBar} this
37531 * @param {Number} newSize the new width or height
37535 * @event beforeresize
37536 * Fires before the splitter is dragged
37537 * @param {Roo.bootstrap.SplitBar} this
37539 "beforeresize" : true,
37541 "beforeapply" : true
37544 Roo.util.Observable.call(this);
37547 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37548 onStartProxyDrag : function(x, y){
37549 this.fireEvent("beforeresize", this);
37551 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37553 o.enableDisplayMode("block");
37554 // all splitbars share the same overlay
37555 Roo.bootstrap.SplitBar.prototype.overlay = o;
37557 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37558 this.overlay.show();
37559 Roo.get(this.proxy).setDisplayed("block");
37560 var size = this.adapter.getElementSize(this);
37561 this.activeMinSize = this.getMinimumSize();;
37562 this.activeMaxSize = this.getMaximumSize();;
37563 var c1 = size - this.activeMinSize;
37564 var c2 = Math.max(this.activeMaxSize - size, 0);
37565 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37566 this.dd.resetConstraints();
37567 this.dd.setXConstraint(
37568 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37569 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37571 this.dd.setYConstraint(0, 0);
37573 this.dd.resetConstraints();
37574 this.dd.setXConstraint(0, 0);
37575 this.dd.setYConstraint(
37576 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37577 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37580 this.dragSpecs.startSize = size;
37581 this.dragSpecs.startPoint = [x, y];
37582 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37586 * @private Called after the drag operation by the DDProxy
37588 onEndProxyDrag : function(e){
37589 Roo.get(this.proxy).setDisplayed(false);
37590 var endPoint = Roo.lib.Event.getXY(e);
37592 this.overlay.hide();
37595 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37596 newSize = this.dragSpecs.startSize +
37597 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37598 endPoint[0] - this.dragSpecs.startPoint[0] :
37599 this.dragSpecs.startPoint[0] - endPoint[0]
37602 newSize = this.dragSpecs.startSize +
37603 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37604 endPoint[1] - this.dragSpecs.startPoint[1] :
37605 this.dragSpecs.startPoint[1] - endPoint[1]
37608 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37609 if(newSize != this.dragSpecs.startSize){
37610 if(this.fireEvent('beforeapply', this, newSize) !== false){
37611 this.adapter.setElementSize(this, newSize);
37612 this.fireEvent("moved", this, newSize);
37613 this.fireEvent("resize", this, newSize);
37619 * Get the adapter this SplitBar uses
37620 * @return The adapter object
37622 getAdapter : function(){
37623 return this.adapter;
37627 * Set the adapter this SplitBar uses
37628 * @param {Object} adapter A SplitBar adapter object
37630 setAdapter : function(adapter){
37631 this.adapter = adapter;
37632 this.adapter.init(this);
37636 * Gets the minimum size for the resizing element
37637 * @return {Number} The minimum size
37639 getMinimumSize : function(){
37640 return this.minSize;
37644 * Sets the minimum size for the resizing element
37645 * @param {Number} minSize The minimum size
37647 setMinimumSize : function(minSize){
37648 this.minSize = minSize;
37652 * Gets the maximum size for the resizing element
37653 * @return {Number} The maximum size
37655 getMaximumSize : function(){
37656 return this.maxSize;
37660 * Sets the maximum size for the resizing element
37661 * @param {Number} maxSize The maximum size
37663 setMaximumSize : function(maxSize){
37664 this.maxSize = maxSize;
37668 * Sets the initialize size for the resizing element
37669 * @param {Number} size The initial size
37671 setCurrentSize : function(size){
37672 var oldAnimate = this.animate;
37673 this.animate = false;
37674 this.adapter.setElementSize(this, size);
37675 this.animate = oldAnimate;
37679 * Destroy this splitbar.
37680 * @param {Boolean} removeEl True to remove the element
37682 destroy : function(removeEl){
37684 this.shim.remove();
37687 this.proxy.parentNode.removeChild(this.proxy);
37695 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
37697 Roo.bootstrap.SplitBar.createProxy = function(dir){
37698 var proxy = new Roo.Element(document.createElement("div"));
37699 proxy.unselectable();
37700 var cls = 'roo-splitbar-proxy';
37701 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37702 document.body.appendChild(proxy.dom);
37707 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37708 * Default Adapter. It assumes the splitter and resizing element are not positioned
37709 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37711 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37714 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37715 // do nothing for now
37716 init : function(s){
37720 * Called before drag operations to get the current size of the resizing element.
37721 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37723 getElementSize : function(s){
37724 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37725 return s.resizingEl.getWidth();
37727 return s.resizingEl.getHeight();
37732 * Called after drag operations to set the size of the resizing element.
37733 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37734 * @param {Number} newSize The new size to set
37735 * @param {Function} onComplete A function to be invoked when resizing is complete
37737 setElementSize : function(s, newSize, onComplete){
37738 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37740 s.resizingEl.setWidth(newSize);
37742 onComplete(s, newSize);
37745 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37750 s.resizingEl.setHeight(newSize);
37752 onComplete(s, newSize);
37755 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37762 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37763 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37764 * Adapter that moves the splitter element to align with the resized sizing element.
37765 * Used with an absolute positioned SplitBar.
37766 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37767 * document.body, make sure you assign an id to the body element.
37769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37770 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37771 this.container = Roo.get(container);
37774 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37775 init : function(s){
37776 this.basic.init(s);
37779 getElementSize : function(s){
37780 return this.basic.getElementSize(s);
37783 setElementSize : function(s, newSize, onComplete){
37784 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37787 moveSplitter : function(s){
37788 var yes = Roo.bootstrap.SplitBar;
37789 switch(s.placement){
37791 s.el.setX(s.resizingEl.getRight());
37794 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37797 s.el.setY(s.resizingEl.getBottom());
37800 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37807 * Orientation constant - Create a vertical SplitBar
37811 Roo.bootstrap.SplitBar.VERTICAL = 1;
37814 * Orientation constant - Create a horizontal SplitBar
37818 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37821 * Placement constant - The resizing element is to the left of the splitter element
37825 Roo.bootstrap.SplitBar.LEFT = 1;
37828 * Placement constant - The resizing element is to the right of the splitter element
37832 Roo.bootstrap.SplitBar.RIGHT = 2;
37835 * Placement constant - The resizing element is positioned above the splitter element
37839 Roo.bootstrap.SplitBar.TOP = 3;
37842 * Placement constant - The resizing element is positioned under splitter element
37846 Roo.bootstrap.SplitBar.BOTTOM = 4;
37847 Roo.namespace("Roo.bootstrap.layout");/*
37849 * Ext JS Library 1.1.1
37850 * Copyright(c) 2006-2007, Ext JS, LLC.
37852 * Originally Released Under LGPL - original licence link has changed is not relivant.
37855 * <script type="text/javascript">
37859 * @class Roo.bootstrap.layout.Manager
37860 * @extends Roo.bootstrap.Component
37861 * Base class for layout managers.
37863 Roo.bootstrap.layout.Manager = function(config)
37865 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37871 /** false to disable window resize monitoring @type Boolean */
37872 this.monitorWindowResize = true;
37877 * Fires when a layout is performed.
37878 * @param {Roo.LayoutManager} this
37882 * @event regionresized
37883 * Fires when the user resizes a region.
37884 * @param {Roo.LayoutRegion} region The resized region
37885 * @param {Number} newSize The new size (width for east/west, height for north/south)
37887 "regionresized" : true,
37889 * @event regioncollapsed
37890 * Fires when a region is collapsed.
37891 * @param {Roo.LayoutRegion} region The collapsed region
37893 "regioncollapsed" : true,
37895 * @event regionexpanded
37896 * Fires when a region is expanded.
37897 * @param {Roo.LayoutRegion} region The expanded region
37899 "regionexpanded" : true
37901 this.updating = false;
37904 this.el = Roo.get(config.el);
37910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37915 monitorWindowResize : true,
37921 onRender : function(ct, position)
37924 this.el = Roo.get(ct);
37927 //this.fireEvent('render',this);
37931 initEvents: function()
37935 // ie scrollbar fix
37936 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37937 document.body.scroll = "no";
37938 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37939 this.el.position('relative');
37941 this.id = this.el.id;
37942 this.el.addClass("roo-layout-container");
37943 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37944 if(this.el.dom != document.body ) {
37945 this.el.on('resize', this.layout,this);
37946 this.el.on('show', this.layout,this);
37952 * Returns true if this layout is currently being updated
37953 * @return {Boolean}
37955 isUpdating : function(){
37956 return this.updating;
37960 * Suspend the LayoutManager from doing auto-layouts while
37961 * making multiple add or remove calls
37963 beginUpdate : function(){
37964 this.updating = true;
37968 * Restore auto-layouts and optionally disable the manager from performing a layout
37969 * @param {Boolean} noLayout true to disable a layout update
37971 endUpdate : function(noLayout){
37972 this.updating = false;
37978 layout: function(){
37982 onRegionResized : function(region, newSize){
37983 this.fireEvent("regionresized", region, newSize);
37987 onRegionCollapsed : function(region){
37988 this.fireEvent("regioncollapsed", region);
37991 onRegionExpanded : function(region){
37992 this.fireEvent("regionexpanded", region);
37996 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37997 * performs box-model adjustments.
37998 * @return {Object} The size as an object {width: (the width), height: (the height)}
38000 getViewSize : function()
38003 if(this.el.dom != document.body){
38004 size = this.el.getSize();
38006 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38008 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38009 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38014 * Returns the Element this layout is bound to.
38015 * @return {Roo.Element}
38017 getEl : function(){
38022 * Returns the specified region.
38023 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38024 * @return {Roo.LayoutRegion}
38026 getRegion : function(target){
38027 return this.regions[target.toLowerCase()];
38030 onWindowResize : function(){
38031 if(this.monitorWindowResize){
38038 * Ext JS Library 1.1.1
38039 * Copyright(c) 2006-2007, Ext JS, LLC.
38041 * Originally Released Under LGPL - original licence link has changed is not relivant.
38044 * <script type="text/javascript">
38047 * @class Roo.bootstrap.layout.Border
38048 * @extends Roo.bootstrap.layout.Manager
38049 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38050 * please see: examples/bootstrap/nested.html<br><br>
38052 <b>The container the layout is rendered into can be either the body element or any other element.
38053 If it is not the body element, the container needs to either be an absolute positioned element,
38054 or you will need to add "position:relative" to the css of the container. You will also need to specify
38055 the container size if it is not the body element.</b>
38058 * Create a new Border
38059 * @param {Object} config Configuration options
38061 Roo.bootstrap.layout.Border = function(config){
38062 config = config || {};
38063 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38067 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38068 if(config[region]){
38069 config[region].region = region;
38070 this.addRegion(config[region]);
38076 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38078 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38080 parent : false, // this might point to a 'nest' or a ???
38083 * Creates and adds a new region if it doesn't already exist.
38084 * @param {String} target The target region key (north, south, east, west or center).
38085 * @param {Object} config The regions config object
38086 * @return {BorderLayoutRegion} The new region
38088 addRegion : function(config)
38090 if(!this.regions[config.region]){
38091 var r = this.factory(config);
38092 this.bindRegion(r);
38094 return this.regions[config.region];
38098 bindRegion : function(r){
38099 this.regions[r.config.region] = r;
38101 r.on("visibilitychange", this.layout, this);
38102 r.on("paneladded", this.layout, this);
38103 r.on("panelremoved", this.layout, this);
38104 r.on("invalidated", this.layout, this);
38105 r.on("resized", this.onRegionResized, this);
38106 r.on("collapsed", this.onRegionCollapsed, this);
38107 r.on("expanded", this.onRegionExpanded, this);
38111 * Performs a layout update.
38113 layout : function()
38115 if(this.updating) {
38119 // render all the rebions if they have not been done alreayd?
38120 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38121 if(this.regions[region] && !this.regions[region].bodyEl){
38122 this.regions[region].onRender(this.el)
38126 var size = this.getViewSize();
38127 var w = size.width;
38128 var h = size.height;
38133 //var x = 0, y = 0;
38135 var rs = this.regions;
38136 var north = rs["north"];
38137 var south = rs["south"];
38138 var west = rs["west"];
38139 var east = rs["east"];
38140 var center = rs["center"];
38141 //if(this.hideOnLayout){ // not supported anymore
38142 //c.el.setStyle("display", "none");
38144 if(north && north.isVisible()){
38145 var b = north.getBox();
38146 var m = north.getMargins();
38147 b.width = w - (m.left+m.right);
38150 centerY = b.height + b.y + m.bottom;
38151 centerH -= centerY;
38152 north.updateBox(this.safeBox(b));
38154 if(south && south.isVisible()){
38155 var b = south.getBox();
38156 var m = south.getMargins();
38157 b.width = w - (m.left+m.right);
38159 var totalHeight = (b.height + m.top + m.bottom);
38160 b.y = h - totalHeight + m.top;
38161 centerH -= totalHeight;
38162 south.updateBox(this.safeBox(b));
38164 if(west && west.isVisible()){
38165 var b = west.getBox();
38166 var m = west.getMargins();
38167 b.height = centerH - (m.top+m.bottom);
38169 b.y = centerY + m.top;
38170 var totalWidth = (b.width + m.left + m.right);
38171 centerX += totalWidth;
38172 centerW -= totalWidth;
38173 west.updateBox(this.safeBox(b));
38175 if(east && east.isVisible()){
38176 var b = east.getBox();
38177 var m = east.getMargins();
38178 b.height = centerH - (m.top+m.bottom);
38179 var totalWidth = (b.width + m.left + m.right);
38180 b.x = w - totalWidth + m.left;
38181 b.y = centerY + m.top;
38182 centerW -= totalWidth;
38183 east.updateBox(this.safeBox(b));
38186 var m = center.getMargins();
38188 x: centerX + m.left,
38189 y: centerY + m.top,
38190 width: centerW - (m.left+m.right),
38191 height: centerH - (m.top+m.bottom)
38193 //if(this.hideOnLayout){
38194 //center.el.setStyle("display", "block");
38196 center.updateBox(this.safeBox(centerBox));
38199 this.fireEvent("layout", this);
38203 safeBox : function(box){
38204 box.width = Math.max(0, box.width);
38205 box.height = Math.max(0, box.height);
38210 * Adds a ContentPanel (or subclass) to this layout.
38211 * @param {String} target The target region key (north, south, east, west or center).
38212 * @param {Roo.ContentPanel} panel The panel to add
38213 * @return {Roo.ContentPanel} The added panel
38215 add : function(target, panel){
38217 target = target.toLowerCase();
38218 return this.regions[target].add(panel);
38222 * Remove a ContentPanel (or subclass) to this layout.
38223 * @param {String} target The target region key (north, south, east, west or center).
38224 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38225 * @return {Roo.ContentPanel} The removed panel
38227 remove : function(target, panel){
38228 target = target.toLowerCase();
38229 return this.regions[target].remove(panel);
38233 * Searches all regions for a panel with the specified id
38234 * @param {String} panelId
38235 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38237 findPanel : function(panelId){
38238 var rs = this.regions;
38239 for(var target in rs){
38240 if(typeof rs[target] != "function"){
38241 var p = rs[target].getPanel(panelId);
38251 * Searches all regions for a panel with the specified id and activates (shows) it.
38252 * @param {String/ContentPanel} panelId The panels id or the panel itself
38253 * @return {Roo.ContentPanel} The shown panel or null
38255 showPanel : function(panelId) {
38256 var rs = this.regions;
38257 for(var target in rs){
38258 var r = rs[target];
38259 if(typeof r != "function"){
38260 if(r.hasPanel(panelId)){
38261 return r.showPanel(panelId);
38269 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38270 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38273 restoreState : function(provider){
38275 provider = Roo.state.Manager;
38277 var sm = new Roo.LayoutStateManager();
38278 sm.init(this, provider);
38284 * Adds a xtype elements to the layout.
38288 xtype : 'ContentPanel',
38295 xtype : 'NestedLayoutPanel',
38301 items : [ ... list of content panels or nested layout panels.. ]
38305 * @param {Object} cfg Xtype definition of item to add.
38307 addxtype : function(cfg)
38309 // basically accepts a pannel...
38310 // can accept a layout region..!?!?
38311 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38314 // theory? children can only be panels??
38316 //if (!cfg.xtype.match(/Panel$/)) {
38321 if (typeof(cfg.region) == 'undefined') {
38322 Roo.log("Failed to add Panel, region was not set");
38326 var region = cfg.region;
38332 xitems = cfg.items;
38337 if ( region == 'center') {
38338 Roo.log("Center: " + cfg.title);
38344 case 'Content': // ContentPanel (el, cfg)
38345 case 'Scroll': // ContentPanel (el, cfg)
38347 cfg.autoCreate = cfg.autoCreate || true;
38348 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38350 // var el = this.el.createChild();
38351 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38354 this.add(region, ret);
38358 case 'TreePanel': // our new panel!
38359 cfg.el = this.el.createChild();
38360 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38361 this.add(region, ret);
38366 // create a new Layout (which is a Border Layout...
38368 var clayout = cfg.layout;
38369 clayout.el = this.el.createChild();
38370 clayout.items = clayout.items || [];
38374 // replace this exitems with the clayout ones..
38375 xitems = clayout.items;
38377 // force background off if it's in center...
38378 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38379 cfg.background = false;
38381 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38384 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38385 //console.log('adding nested layout panel ' + cfg.toSource());
38386 this.add(region, ret);
38387 nb = {}; /// find first...
38392 // needs grid and region
38394 //var el = this.getRegion(region).el.createChild();
38396 *var el = this.el.createChild();
38397 // create the grid first...
38398 cfg.grid.container = el;
38399 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38402 if (region == 'center' && this.active ) {
38403 cfg.background = false;
38406 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38408 this.add(region, ret);
38410 if (cfg.background) {
38411 // render grid on panel activation (if panel background)
38412 ret.on('activate', function(gp) {
38413 if (!gp.grid.rendered) {
38414 // gp.grid.render(el);
38418 // cfg.grid.render(el);
38424 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38425 // it was the old xcomponent building that caused this before.
38426 // espeically if border is the top element in the tree.
38436 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38438 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38439 this.add(region, ret);
38443 throw "Can not add '" + cfg.xtype + "' to Border";
38449 this.beginUpdate();
38453 Roo.each(xitems, function(i) {
38454 region = nb && i.region ? i.region : false;
38456 var add = ret.addxtype(i);
38459 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38460 if (!i.background) {
38461 abn[region] = nb[region] ;
38468 // make the last non-background panel active..
38469 //if (nb) { Roo.log(abn); }
38472 for(var r in abn) {
38473 region = this.getRegion(r);
38475 // tried using nb[r], but it does not work..
38477 region.showPanel(abn[r]);
38488 factory : function(cfg)
38491 var validRegions = Roo.bootstrap.layout.Border.regions;
38493 var target = cfg.region;
38496 var r = Roo.bootstrap.layout;
38500 return new r.North(cfg);
38502 return new r.South(cfg);
38504 return new r.East(cfg);
38506 return new r.West(cfg);
38508 return new r.Center(cfg);
38510 throw 'Layout region "'+target+'" not supported.';
38517 * Ext JS Library 1.1.1
38518 * Copyright(c) 2006-2007, Ext JS, LLC.
38520 * Originally Released Under LGPL - original licence link has changed is not relivant.
38523 * <script type="text/javascript">
38527 * @class Roo.bootstrap.layout.Basic
38528 * @extends Roo.util.Observable
38529 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38530 * and does not have a titlebar, tabs or any other features. All it does is size and position
38531 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38532 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38533 * @cfg {string} region the region that it inhabits..
38534 * @cfg {bool} skipConfig skip config?
38538 Roo.bootstrap.layout.Basic = function(config){
38540 this.mgr = config.mgr;
38542 this.position = config.region;
38544 var skipConfig = config.skipConfig;
38548 * @scope Roo.BasicLayoutRegion
38552 * @event beforeremove
38553 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38554 * @param {Roo.LayoutRegion} this
38555 * @param {Roo.ContentPanel} panel The panel
38556 * @param {Object} e The cancel event object
38558 "beforeremove" : true,
38560 * @event invalidated
38561 * Fires when the layout for this region is changed.
38562 * @param {Roo.LayoutRegion} this
38564 "invalidated" : true,
38566 * @event visibilitychange
38567 * Fires when this region is shown or hidden
38568 * @param {Roo.LayoutRegion} this
38569 * @param {Boolean} visibility true or false
38571 "visibilitychange" : true,
38573 * @event paneladded
38574 * Fires when a panel is added.
38575 * @param {Roo.LayoutRegion} this
38576 * @param {Roo.ContentPanel} panel The panel
38578 "paneladded" : true,
38580 * @event panelremoved
38581 * Fires when a panel is removed.
38582 * @param {Roo.LayoutRegion} this
38583 * @param {Roo.ContentPanel} panel The panel
38585 "panelremoved" : true,
38587 * @event beforecollapse
38588 * Fires when this region before collapse.
38589 * @param {Roo.LayoutRegion} this
38591 "beforecollapse" : true,
38594 * Fires when this region is collapsed.
38595 * @param {Roo.LayoutRegion} this
38597 "collapsed" : true,
38600 * Fires when this region is expanded.
38601 * @param {Roo.LayoutRegion} this
38606 * Fires when this region is slid into view.
38607 * @param {Roo.LayoutRegion} this
38609 "slideshow" : true,
38612 * Fires when this region slides out of view.
38613 * @param {Roo.LayoutRegion} this
38615 "slidehide" : true,
38617 * @event panelactivated
38618 * Fires when a panel is activated.
38619 * @param {Roo.LayoutRegion} this
38620 * @param {Roo.ContentPanel} panel The activated panel
38622 "panelactivated" : true,
38625 * Fires when the user resizes this region.
38626 * @param {Roo.LayoutRegion} this
38627 * @param {Number} newSize The new size (width for east/west, height for north/south)
38631 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38632 this.panels = new Roo.util.MixedCollection();
38633 this.panels.getKey = this.getPanelId.createDelegate(this);
38635 this.activePanel = null;
38636 // ensure listeners are added...
38638 if (config.listeners || config.events) {
38639 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38640 listeners : config.listeners || {},
38641 events : config.events || {}
38645 if(skipConfig !== true){
38646 this.applyConfig(config);
38650 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38652 getPanelId : function(p){
38656 applyConfig : function(config){
38657 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38658 this.config = config;
38663 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38664 * the width, for horizontal (north, south) the height.
38665 * @param {Number} newSize The new width or height
38667 resizeTo : function(newSize){
38668 var el = this.el ? this.el :
38669 (this.activePanel ? this.activePanel.getEl() : null);
38671 switch(this.position){
38674 el.setWidth(newSize);
38675 this.fireEvent("resized", this, newSize);
38679 el.setHeight(newSize);
38680 this.fireEvent("resized", this, newSize);
38686 getBox : function(){
38687 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38690 getMargins : function(){
38691 return this.margins;
38694 updateBox : function(box){
38696 var el = this.activePanel.getEl();
38697 el.dom.style.left = box.x + "px";
38698 el.dom.style.top = box.y + "px";
38699 this.activePanel.setSize(box.width, box.height);
38703 * Returns the container element for this region.
38704 * @return {Roo.Element}
38706 getEl : function(){
38707 return this.activePanel;
38711 * Returns true if this region is currently visible.
38712 * @return {Boolean}
38714 isVisible : function(){
38715 return this.activePanel ? true : false;
38718 setActivePanel : function(panel){
38719 panel = this.getPanel(panel);
38720 if(this.activePanel && this.activePanel != panel){
38721 this.activePanel.setActiveState(false);
38722 this.activePanel.getEl().setLeftTop(-10000,-10000);
38724 this.activePanel = panel;
38725 panel.setActiveState(true);
38727 panel.setSize(this.box.width, this.box.height);
38729 this.fireEvent("panelactivated", this, panel);
38730 this.fireEvent("invalidated");
38734 * Show the specified panel.
38735 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38736 * @return {Roo.ContentPanel} The shown panel or null
38738 showPanel : function(panel){
38739 panel = this.getPanel(panel);
38741 this.setActivePanel(panel);
38747 * Get the active panel for this region.
38748 * @return {Roo.ContentPanel} The active panel or null
38750 getActivePanel : function(){
38751 return this.activePanel;
38755 * Add the passed ContentPanel(s)
38756 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38757 * @return {Roo.ContentPanel} The panel added (if only one was added)
38759 add : function(panel){
38760 if(arguments.length > 1){
38761 for(var i = 0, len = arguments.length; i < len; i++) {
38762 this.add(arguments[i]);
38766 if(this.hasPanel(panel)){
38767 this.showPanel(panel);
38770 var el = panel.getEl();
38771 if(el.dom.parentNode != this.mgr.el.dom){
38772 this.mgr.el.dom.appendChild(el.dom);
38774 if(panel.setRegion){
38775 panel.setRegion(this);
38777 this.panels.add(panel);
38778 el.setStyle("position", "absolute");
38779 if(!panel.background){
38780 this.setActivePanel(panel);
38781 if(this.config.initialSize && this.panels.getCount()==1){
38782 this.resizeTo(this.config.initialSize);
38785 this.fireEvent("paneladded", this, panel);
38790 * Returns true if the panel is in this region.
38791 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38792 * @return {Boolean}
38794 hasPanel : function(panel){
38795 if(typeof panel == "object"){ // must be panel obj
38796 panel = panel.getId();
38798 return this.getPanel(panel) ? true : false;
38802 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38803 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804 * @param {Boolean} preservePanel Overrides the config preservePanel option
38805 * @return {Roo.ContentPanel} The panel that was removed
38807 remove : function(panel, preservePanel){
38808 panel = this.getPanel(panel);
38813 this.fireEvent("beforeremove", this, panel, e);
38814 if(e.cancel === true){
38817 var panelId = panel.getId();
38818 this.panels.removeKey(panelId);
38823 * Returns the panel specified or null if it's not in this region.
38824 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38825 * @return {Roo.ContentPanel}
38827 getPanel : function(id){
38828 if(typeof id == "object"){ // must be panel obj
38831 return this.panels.get(id);
38835 * Returns this regions position (north/south/east/west/center).
38838 getPosition: function(){
38839 return this.position;
38843 * Ext JS Library 1.1.1
38844 * Copyright(c) 2006-2007, Ext JS, LLC.
38846 * Originally Released Under LGPL - original licence link has changed is not relivant.
38849 * <script type="text/javascript">
38853 * @class Roo.bootstrap.layout.Region
38854 * @extends Roo.bootstrap.layout.Basic
38855 * This class represents a region in a layout manager.
38857 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38858 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
38859 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38860 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38861 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38862 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38863 * @cfg {String} title The title for the region (overrides panel titles)
38864 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38865 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38866 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38867 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38868 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38869 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38870 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38871 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38872 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38873 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38875 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38876 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38877 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38878 * @cfg {Number} width For East/West panels
38879 * @cfg {Number} height For North/South panels
38880 * @cfg {Boolean} split To show the splitter
38881 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38883 * @cfg {string} cls Extra CSS classes to add to region
38885 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38886 * @cfg {string} region the region that it inhabits..
38889 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38890 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38892 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38893 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38894 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38896 Roo.bootstrap.layout.Region = function(config)
38898 this.applyConfig(config);
38900 var mgr = config.mgr;
38901 var pos = config.region;
38902 config.skipConfig = true;
38903 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38906 this.onRender(mgr.el);
38909 this.visible = true;
38910 this.collapsed = false;
38911 this.unrendered_panels = [];
38914 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38916 position: '', // set by wrapper (eg. north/south etc..)
38917 unrendered_panels : null, // unrendered panels.
38919 tabPosition : false,
38921 mgr: false, // points to 'Border'
38924 createBody : function(){
38925 /** This region's body element
38926 * @type Roo.Element */
38927 this.bodyEl = this.el.createChild({
38929 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38933 onRender: function(ctr, pos)
38935 var dh = Roo.DomHelper;
38936 /** This region's container element
38937 * @type Roo.Element */
38938 this.el = dh.append(ctr.dom, {
38940 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38942 /** This region's title element
38943 * @type Roo.Element */
38945 this.titleEl = dh.append(this.el.dom, {
38947 unselectable: "on",
38948 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38950 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38951 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38955 this.titleEl.enableDisplayMode();
38956 /** This region's title text element
38957 * @type HTMLElement */
38958 this.titleTextEl = this.titleEl.dom.firstChild;
38959 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38961 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38962 this.closeBtn.enableDisplayMode();
38963 this.closeBtn.on("click", this.closeClicked, this);
38964 this.closeBtn.hide();
38966 this.createBody(this.config);
38967 if(this.config.hideWhenEmpty){
38969 this.on("paneladded", this.validateVisibility, this);
38970 this.on("panelremoved", this.validateVisibility, this);
38972 if(this.autoScroll){
38973 this.bodyEl.setStyle("overflow", "auto");
38975 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38977 //if(c.titlebar !== false){
38978 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38979 this.titleEl.hide();
38981 this.titleEl.show();
38982 if(this.config.title){
38983 this.titleTextEl.innerHTML = this.config.title;
38987 if(this.config.collapsed){
38988 this.collapse(true);
38990 if(this.config.hidden){
38994 if (this.unrendered_panels && this.unrendered_panels.length) {
38995 for (var i =0;i< this.unrendered_panels.length; i++) {
38996 this.add(this.unrendered_panels[i]);
38998 this.unrendered_panels = null;
39004 applyConfig : function(c)
39007 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39008 var dh = Roo.DomHelper;
39009 if(c.titlebar !== false){
39010 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39011 this.collapseBtn.on("click", this.collapse, this);
39012 this.collapseBtn.enableDisplayMode();
39014 if(c.showPin === true || this.showPin){
39015 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39016 this.stickBtn.enableDisplayMode();
39017 this.stickBtn.on("click", this.expand, this);
39018 this.stickBtn.hide();
39023 /** This region's collapsed element
39024 * @type Roo.Element */
39027 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39028 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39031 if(c.floatable !== false){
39032 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39033 this.collapsedEl.on("click", this.collapseClick, this);
39036 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39037 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39038 id: "message", unselectable: "on", style:{"float":"left"}});
39039 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39041 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39042 this.expandBtn.on("click", this.expand, this);
39046 if(this.collapseBtn){
39047 this.collapseBtn.setVisible(c.collapsible == true);
39050 this.cmargins = c.cmargins || this.cmargins ||
39051 (this.position == "west" || this.position == "east" ?
39052 {top: 0, left: 2, right:2, bottom: 0} :
39053 {top: 2, left: 0, right:0, bottom: 2});
39055 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39058 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39060 this.autoScroll = c.autoScroll || false;
39065 this.duration = c.duration || .30;
39066 this.slideDuration = c.slideDuration || .45;
39071 * Returns true if this region is currently visible.
39072 * @return {Boolean}
39074 isVisible : function(){
39075 return this.visible;
39079 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39080 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39082 //setCollapsedTitle : function(title){
39083 // title = title || " ";
39084 // if(this.collapsedTitleTextEl){
39085 // this.collapsedTitleTextEl.innerHTML = title;
39089 getBox : function(){
39091 // if(!this.collapsed){
39092 b = this.el.getBox(false, true);
39094 // b = this.collapsedEl.getBox(false, true);
39099 getMargins : function(){
39100 return this.margins;
39101 //return this.collapsed ? this.cmargins : this.margins;
39104 highlight : function(){
39105 this.el.addClass("x-layout-panel-dragover");
39108 unhighlight : function(){
39109 this.el.removeClass("x-layout-panel-dragover");
39112 updateBox : function(box)
39114 if (!this.bodyEl) {
39115 return; // not rendered yet..
39119 if(!this.collapsed){
39120 this.el.dom.style.left = box.x + "px";
39121 this.el.dom.style.top = box.y + "px";
39122 this.updateBody(box.width, box.height);
39124 this.collapsedEl.dom.style.left = box.x + "px";
39125 this.collapsedEl.dom.style.top = box.y + "px";
39126 this.collapsedEl.setSize(box.width, box.height);
39129 this.tabs.autoSizeTabs();
39133 updateBody : function(w, h)
39136 this.el.setWidth(w);
39137 w -= this.el.getBorderWidth("rl");
39138 if(this.config.adjustments){
39139 w += this.config.adjustments[0];
39142 if(h !== null && h > 0){
39143 this.el.setHeight(h);
39144 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39145 h -= this.el.getBorderWidth("tb");
39146 if(this.config.adjustments){
39147 h += this.config.adjustments[1];
39149 this.bodyEl.setHeight(h);
39151 h = this.tabs.syncHeight(h);
39154 if(this.panelSize){
39155 w = w !== null ? w : this.panelSize.width;
39156 h = h !== null ? h : this.panelSize.height;
39158 if(this.activePanel){
39159 var el = this.activePanel.getEl();
39160 w = w !== null ? w : el.getWidth();
39161 h = h !== null ? h : el.getHeight();
39162 this.panelSize = {width: w, height: h};
39163 this.activePanel.setSize(w, h);
39165 if(Roo.isIE && this.tabs){
39166 this.tabs.el.repaint();
39171 * Returns the container element for this region.
39172 * @return {Roo.Element}
39174 getEl : function(){
39179 * Hides this region.
39182 //if(!this.collapsed){
39183 this.el.dom.style.left = "-2000px";
39186 // this.collapsedEl.dom.style.left = "-2000px";
39187 // this.collapsedEl.hide();
39189 this.visible = false;
39190 this.fireEvent("visibilitychange", this, false);
39194 * Shows this region if it was previously hidden.
39197 //if(!this.collapsed){
39200 // this.collapsedEl.show();
39202 this.visible = true;
39203 this.fireEvent("visibilitychange", this, true);
39206 closeClicked : function(){
39207 if(this.activePanel){
39208 this.remove(this.activePanel);
39212 collapseClick : function(e){
39214 e.stopPropagation();
39217 e.stopPropagation();
39223 * Collapses this region.
39224 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39227 collapse : function(skipAnim, skipCheck = false){
39228 if(this.collapsed) {
39232 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39234 this.collapsed = true;
39236 this.split.el.hide();
39238 if(this.config.animate && skipAnim !== true){
39239 this.fireEvent("invalidated", this);
39240 this.animateCollapse();
39242 this.el.setLocation(-20000,-20000);
39244 this.collapsedEl.show();
39245 this.fireEvent("collapsed", this);
39246 this.fireEvent("invalidated", this);
39252 animateCollapse : function(){
39257 * Expands this region if it was previously collapsed.
39258 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39259 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39262 expand : function(e, skipAnim){
39264 e.stopPropagation();
39266 if(!this.collapsed || this.el.hasActiveFx()) {
39270 this.afterSlideIn();
39273 this.collapsed = false;
39274 if(this.config.animate && skipAnim !== true){
39275 this.animateExpand();
39279 this.split.el.show();
39281 this.collapsedEl.setLocation(-2000,-2000);
39282 this.collapsedEl.hide();
39283 this.fireEvent("invalidated", this);
39284 this.fireEvent("expanded", this);
39288 animateExpand : function(){
39292 initTabs : function()
39294 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39296 var ts = new Roo.bootstrap.panel.Tabs({
39297 el: this.bodyEl.dom,
39299 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39300 disableTooltips: this.config.disableTabTips,
39301 toolbar : this.config.toolbar
39304 if(this.config.hideTabs){
39305 ts.stripWrap.setDisplayed(false);
39308 ts.resizeTabs = this.config.resizeTabs === true;
39309 ts.minTabWidth = this.config.minTabWidth || 40;
39310 ts.maxTabWidth = this.config.maxTabWidth || 250;
39311 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39312 ts.monitorResize = false;
39313 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39314 ts.bodyEl.addClass('roo-layout-tabs-body');
39315 this.panels.each(this.initPanelAsTab, this);
39318 initPanelAsTab : function(panel){
39319 var ti = this.tabs.addTab(
39323 this.config.closeOnTab && panel.isClosable(),
39326 if(panel.tabTip !== undefined){
39327 ti.setTooltip(panel.tabTip);
39329 ti.on("activate", function(){
39330 this.setActivePanel(panel);
39333 if(this.config.closeOnTab){
39334 ti.on("beforeclose", function(t, e){
39336 this.remove(panel);
39340 panel.tabItem = ti;
39345 updatePanelTitle : function(panel, title)
39347 if(this.activePanel == panel){
39348 this.updateTitle(title);
39351 var ti = this.tabs.getTab(panel.getEl().id);
39353 if(panel.tabTip !== undefined){
39354 ti.setTooltip(panel.tabTip);
39359 updateTitle : function(title){
39360 if(this.titleTextEl && !this.config.title){
39361 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39365 setActivePanel : function(panel)
39367 panel = this.getPanel(panel);
39368 if(this.activePanel && this.activePanel != panel){
39369 if(this.activePanel.setActiveState(false) === false){
39373 this.activePanel = panel;
39374 panel.setActiveState(true);
39375 if(this.panelSize){
39376 panel.setSize(this.panelSize.width, this.panelSize.height);
39379 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39381 this.updateTitle(panel.getTitle());
39383 this.fireEvent("invalidated", this);
39385 this.fireEvent("panelactivated", this, panel);
39389 * Shows the specified panel.
39390 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39391 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39393 showPanel : function(panel)
39395 panel = this.getPanel(panel);
39398 var tab = this.tabs.getTab(panel.getEl().id);
39399 if(tab.isHidden()){
39400 this.tabs.unhideTab(tab.id);
39404 this.setActivePanel(panel);
39411 * Get the active panel for this region.
39412 * @return {Roo.ContentPanel} The active panel or null
39414 getActivePanel : function(){
39415 return this.activePanel;
39418 validateVisibility : function(){
39419 if(this.panels.getCount() < 1){
39420 this.updateTitle(" ");
39421 this.closeBtn.hide();
39424 if(!this.isVisible()){
39431 * Adds the passed ContentPanel(s) to this region.
39432 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39433 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39435 add : function(panel)
39437 if(arguments.length > 1){
39438 for(var i = 0, len = arguments.length; i < len; i++) {
39439 this.add(arguments[i]);
39444 // if we have not been rendered yet, then we can not really do much of this..
39445 if (!this.bodyEl) {
39446 this.unrendered_panels.push(panel);
39453 if(this.hasPanel(panel)){
39454 this.showPanel(panel);
39457 panel.setRegion(this);
39458 this.panels.add(panel);
39459 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39460 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39461 // and hide them... ???
39462 this.bodyEl.dom.appendChild(panel.getEl().dom);
39463 if(panel.background !== true){
39464 this.setActivePanel(panel);
39466 this.fireEvent("paneladded", this, panel);
39473 this.initPanelAsTab(panel);
39477 if(panel.background !== true){
39478 this.tabs.activate(panel.getEl().id);
39480 this.fireEvent("paneladded", this, panel);
39485 * Hides the tab for the specified panel.
39486 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39488 hidePanel : function(panel){
39489 if(this.tabs && (panel = this.getPanel(panel))){
39490 this.tabs.hideTab(panel.getEl().id);
39495 * Unhides the tab for a previously hidden panel.
39496 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39498 unhidePanel : function(panel){
39499 if(this.tabs && (panel = this.getPanel(panel))){
39500 this.tabs.unhideTab(panel.getEl().id);
39504 clearPanels : function(){
39505 while(this.panels.getCount() > 0){
39506 this.remove(this.panels.first());
39511 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39512 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39513 * @param {Boolean} preservePanel Overrides the config preservePanel option
39514 * @return {Roo.ContentPanel} The panel that was removed
39516 remove : function(panel, preservePanel)
39518 panel = this.getPanel(panel);
39523 this.fireEvent("beforeremove", this, panel, e);
39524 if(e.cancel === true){
39527 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39528 var panelId = panel.getId();
39529 this.panels.removeKey(panelId);
39531 document.body.appendChild(panel.getEl().dom);
39534 this.tabs.removeTab(panel.getEl().id);
39535 }else if (!preservePanel){
39536 this.bodyEl.dom.removeChild(panel.getEl().dom);
39538 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39539 var p = this.panels.first();
39540 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39541 tempEl.appendChild(p.getEl().dom);
39542 this.bodyEl.update("");
39543 this.bodyEl.dom.appendChild(p.getEl().dom);
39545 this.updateTitle(p.getTitle());
39547 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39548 this.setActivePanel(p);
39550 panel.setRegion(null);
39551 if(this.activePanel == panel){
39552 this.activePanel = null;
39554 if(this.config.autoDestroy !== false && preservePanel !== true){
39555 try{panel.destroy();}catch(e){}
39557 this.fireEvent("panelremoved", this, panel);
39562 * Returns the TabPanel component used by this region
39563 * @return {Roo.TabPanel}
39565 getTabs : function(){
39569 createTool : function(parentEl, className){
39570 var btn = Roo.DomHelper.append(parentEl, {
39572 cls: "x-layout-tools-button",
39575 cls: "roo-layout-tools-button-inner " + className,
39579 btn.addClassOnOver("roo-layout-tools-button-over");
39584 * Ext JS Library 1.1.1
39585 * Copyright(c) 2006-2007, Ext JS, LLC.
39587 * Originally Released Under LGPL - original licence link has changed is not relivant.
39590 * <script type="text/javascript">
39596 * @class Roo.SplitLayoutRegion
39597 * @extends Roo.LayoutRegion
39598 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39600 Roo.bootstrap.layout.Split = function(config){
39601 this.cursor = config.cursor;
39602 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39605 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39607 splitTip : "Drag to resize.",
39608 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39609 useSplitTips : false,
39611 applyConfig : function(config){
39612 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39615 onRender : function(ctr,pos) {
39617 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39618 if(!this.config.split){
39623 var splitEl = Roo.DomHelper.append(ctr.dom, {
39625 id: this.el.id + "-split",
39626 cls: "roo-layout-split roo-layout-split-"+this.position,
39629 /** The SplitBar for this region
39630 * @type Roo.SplitBar */
39631 // does not exist yet...
39632 Roo.log([this.position, this.orientation]);
39634 this.split = new Roo.bootstrap.SplitBar({
39635 dragElement : splitEl,
39636 resizingElement: this.el,
39637 orientation : this.orientation
39640 this.split.on("moved", this.onSplitMove, this);
39641 this.split.useShim = this.config.useShim === true;
39642 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39643 if(this.useSplitTips){
39644 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39646 //if(config.collapsible){
39647 // this.split.el.on("dblclick", this.collapse, this);
39650 if(typeof this.config.minSize != "undefined"){
39651 this.split.minSize = this.config.minSize;
39653 if(typeof this.config.maxSize != "undefined"){
39654 this.split.maxSize = this.config.maxSize;
39656 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39657 this.hideSplitter();
39662 getHMaxSize : function(){
39663 var cmax = this.config.maxSize || 10000;
39664 var center = this.mgr.getRegion("center");
39665 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39668 getVMaxSize : function(){
39669 var cmax = this.config.maxSize || 10000;
39670 var center = this.mgr.getRegion("center");
39671 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39674 onSplitMove : function(split, newSize){
39675 this.fireEvent("resized", this, newSize);
39679 * Returns the {@link Roo.SplitBar} for this region.
39680 * @return {Roo.SplitBar}
39682 getSplitBar : function(){
39687 this.hideSplitter();
39688 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39691 hideSplitter : function(){
39693 this.split.el.setLocation(-2000,-2000);
39694 this.split.el.hide();
39700 this.split.el.show();
39702 Roo.bootstrap.layout.Split.superclass.show.call(this);
39705 beforeSlide: function(){
39706 if(Roo.isGecko){// firefox overflow auto bug workaround
39707 this.bodyEl.clip();
39709 this.tabs.bodyEl.clip();
39711 if(this.activePanel){
39712 this.activePanel.getEl().clip();
39714 if(this.activePanel.beforeSlide){
39715 this.activePanel.beforeSlide();
39721 afterSlide : function(){
39722 if(Roo.isGecko){// firefox overflow auto bug workaround
39723 this.bodyEl.unclip();
39725 this.tabs.bodyEl.unclip();
39727 if(this.activePanel){
39728 this.activePanel.getEl().unclip();
39729 if(this.activePanel.afterSlide){
39730 this.activePanel.afterSlide();
39736 initAutoHide : function(){
39737 if(this.autoHide !== false){
39738 if(!this.autoHideHd){
39739 var st = new Roo.util.DelayedTask(this.slideIn, this);
39740 this.autoHideHd = {
39741 "mouseout": function(e){
39742 if(!e.within(this.el, true)){
39746 "mouseover" : function(e){
39752 this.el.on(this.autoHideHd);
39756 clearAutoHide : function(){
39757 if(this.autoHide !== false){
39758 this.el.un("mouseout", this.autoHideHd.mouseout);
39759 this.el.un("mouseover", this.autoHideHd.mouseover);
39763 clearMonitor : function(){
39764 Roo.get(document).un("click", this.slideInIf, this);
39767 // these names are backwards but not changed for compat
39768 slideOut : function(){
39769 if(this.isSlid || this.el.hasActiveFx()){
39772 this.isSlid = true;
39773 if(this.collapseBtn){
39774 this.collapseBtn.hide();
39776 this.closeBtnState = this.closeBtn.getStyle('display');
39777 this.closeBtn.hide();
39779 this.stickBtn.show();
39782 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39783 this.beforeSlide();
39784 this.el.setStyle("z-index", 10001);
39785 this.el.slideIn(this.getSlideAnchor(), {
39786 callback: function(){
39788 this.initAutoHide();
39789 Roo.get(document).on("click", this.slideInIf, this);
39790 this.fireEvent("slideshow", this);
39797 afterSlideIn : function(){
39798 this.clearAutoHide();
39799 this.isSlid = false;
39800 this.clearMonitor();
39801 this.el.setStyle("z-index", "");
39802 if(this.collapseBtn){
39803 this.collapseBtn.show();
39805 this.closeBtn.setStyle('display', this.closeBtnState);
39807 this.stickBtn.hide();
39809 this.fireEvent("slidehide", this);
39812 slideIn : function(cb){
39813 if(!this.isSlid || this.el.hasActiveFx()){
39817 this.isSlid = false;
39818 this.beforeSlide();
39819 this.el.slideOut(this.getSlideAnchor(), {
39820 callback: function(){
39821 this.el.setLeftTop(-10000, -10000);
39823 this.afterSlideIn();
39831 slideInIf : function(e){
39832 if(!e.within(this.el)){
39837 animateCollapse : function(){
39838 this.beforeSlide();
39839 this.el.setStyle("z-index", 20000);
39840 var anchor = this.getSlideAnchor();
39841 this.el.slideOut(anchor, {
39842 callback : function(){
39843 this.el.setStyle("z-index", "");
39844 this.collapsedEl.slideIn(anchor, {duration:.3});
39846 this.el.setLocation(-10000,-10000);
39848 this.fireEvent("collapsed", this);
39855 animateExpand : function(){
39856 this.beforeSlide();
39857 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39858 this.el.setStyle("z-index", 20000);
39859 this.collapsedEl.hide({
39862 this.el.slideIn(this.getSlideAnchor(), {
39863 callback : function(){
39864 this.el.setStyle("z-index", "");
39867 this.split.el.show();
39869 this.fireEvent("invalidated", this);
39870 this.fireEvent("expanded", this);
39898 getAnchor : function(){
39899 return this.anchors[this.position];
39902 getCollapseAnchor : function(){
39903 return this.canchors[this.position];
39906 getSlideAnchor : function(){
39907 return this.sanchors[this.position];
39910 getAlignAdj : function(){
39911 var cm = this.cmargins;
39912 switch(this.position){
39928 getExpandAdj : function(){
39929 var c = this.collapsedEl, cm = this.cmargins;
39930 switch(this.position){
39932 return [-(cm.right+c.getWidth()+cm.left), 0];
39935 return [cm.right+c.getWidth()+cm.left, 0];
39938 return [0, -(cm.top+cm.bottom+c.getHeight())];
39941 return [0, cm.top+cm.bottom+c.getHeight()];
39947 * Ext JS Library 1.1.1
39948 * Copyright(c) 2006-2007, Ext JS, LLC.
39950 * Originally Released Under LGPL - original licence link has changed is not relivant.
39953 * <script type="text/javascript">
39956 * These classes are private internal classes
39958 Roo.bootstrap.layout.Center = function(config){
39959 config.region = "center";
39960 Roo.bootstrap.layout.Region.call(this, config);
39961 this.visible = true;
39962 this.minWidth = config.minWidth || 20;
39963 this.minHeight = config.minHeight || 20;
39966 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39968 // center panel can't be hidden
39972 // center panel can't be hidden
39975 getMinWidth: function(){
39976 return this.minWidth;
39979 getMinHeight: function(){
39980 return this.minHeight;
39994 Roo.bootstrap.layout.North = function(config)
39996 config.region = 'north';
39997 config.cursor = 'n-resize';
39999 Roo.bootstrap.layout.Split.call(this, config);
40003 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40004 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40005 this.split.el.addClass("roo-layout-split-v");
40007 //var size = config.initialSize || config.height;
40008 //if(this.el && typeof size != "undefined"){
40009 // this.el.setHeight(size);
40012 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40014 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40017 onRender : function(ctr, pos)
40019 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40020 var size = this.config.initialSize || this.config.height;
40021 if(this.el && typeof size != "undefined"){
40022 this.el.setHeight(size);
40027 getBox : function(){
40028 if(this.collapsed){
40029 return this.collapsedEl.getBox();
40031 var box = this.el.getBox();
40033 box.height += this.split.el.getHeight();
40038 updateBox : function(box){
40039 if(this.split && !this.collapsed){
40040 box.height -= this.split.el.getHeight();
40041 this.split.el.setLeft(box.x);
40042 this.split.el.setTop(box.y+box.height);
40043 this.split.el.setWidth(box.width);
40045 if(this.collapsed){
40046 this.updateBody(box.width, null);
40048 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40056 Roo.bootstrap.layout.South = function(config){
40057 config.region = 'south';
40058 config.cursor = 's-resize';
40059 Roo.bootstrap.layout.Split.call(this, config);
40061 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40062 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40063 this.split.el.addClass("roo-layout-split-v");
40068 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40069 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40071 onRender : function(ctr, pos)
40073 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40074 var size = this.config.initialSize || this.config.height;
40075 if(this.el && typeof size != "undefined"){
40076 this.el.setHeight(size);
40081 getBox : function(){
40082 if(this.collapsed){
40083 return this.collapsedEl.getBox();
40085 var box = this.el.getBox();
40087 var sh = this.split.el.getHeight();
40094 updateBox : function(box){
40095 if(this.split && !this.collapsed){
40096 var sh = this.split.el.getHeight();
40099 this.split.el.setLeft(box.x);
40100 this.split.el.setTop(box.y-sh);
40101 this.split.el.setWidth(box.width);
40103 if(this.collapsed){
40104 this.updateBody(box.width, null);
40106 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40110 Roo.bootstrap.layout.East = function(config){
40111 config.region = "east";
40112 config.cursor = "e-resize";
40113 Roo.bootstrap.layout.Split.call(this, config);
40115 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40116 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40117 this.split.el.addClass("roo-layout-split-h");
40121 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40122 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40124 onRender : function(ctr, pos)
40126 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40127 var size = this.config.initialSize || this.config.width;
40128 if(this.el && typeof size != "undefined"){
40129 this.el.setWidth(size);
40134 getBox : function(){
40135 if(this.collapsed){
40136 return this.collapsedEl.getBox();
40138 var box = this.el.getBox();
40140 var sw = this.split.el.getWidth();
40147 updateBox : function(box){
40148 if(this.split && !this.collapsed){
40149 var sw = this.split.el.getWidth();
40151 this.split.el.setLeft(box.x);
40152 this.split.el.setTop(box.y);
40153 this.split.el.setHeight(box.height);
40156 if(this.collapsed){
40157 this.updateBody(null, box.height);
40159 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40163 Roo.bootstrap.layout.West = function(config){
40164 config.region = "west";
40165 config.cursor = "w-resize";
40167 Roo.bootstrap.layout.Split.call(this, config);
40169 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40170 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40171 this.split.el.addClass("roo-layout-split-h");
40175 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40176 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40178 onRender: function(ctr, pos)
40180 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40181 var size = this.config.initialSize || this.config.width;
40182 if(typeof size != "undefined"){
40183 this.el.setWidth(size);
40187 getBox : function(){
40188 if(this.collapsed){
40189 return this.collapsedEl.getBox();
40191 var box = this.el.getBox();
40192 if (box.width == 0) {
40193 box.width = this.config.width; // kludge?
40196 box.width += this.split.el.getWidth();
40201 updateBox : function(box){
40202 if(this.split && !this.collapsed){
40203 var sw = this.split.el.getWidth();
40205 this.split.el.setLeft(box.x+box.width);
40206 this.split.el.setTop(box.y);
40207 this.split.el.setHeight(box.height);
40209 if(this.collapsed){
40210 this.updateBody(null, box.height);
40212 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40214 });Roo.namespace("Roo.bootstrap.panel");/*
40216 * Ext JS Library 1.1.1
40217 * Copyright(c) 2006-2007, Ext JS, LLC.
40219 * Originally Released Under LGPL - original licence link has changed is not relivant.
40222 * <script type="text/javascript">
40225 * @class Roo.ContentPanel
40226 * @extends Roo.util.Observable
40227 * A basic ContentPanel element.
40228 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40229 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40230 * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
40231 * @cfg {Boolean} closable True if the panel can be closed/removed
40232 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40233 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40234 * @cfg {Toolbar} toolbar A toolbar for this panel
40235 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40236 * @cfg {String} title The title for this panel
40237 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40238 * @cfg {String} url Calls {@link #setUrl} with this value
40239 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40240 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40241 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40242 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40243 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40244 * @cfg {Boolean} badges render the badges
40245 * @cfg {String} cls extra classes to use
40246 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40249 * Create a new ContentPanel.
40250 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40251 * @param {String/Object} config A string to set only the title or a config object
40252 * @param {String} content (optional) Set the HTML content for this panel
40253 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40255 Roo.bootstrap.panel.Content = function( config){
40257 this.tpl = config.tpl || false;
40259 var el = config.el;
40260 var content = config.content;
40262 if(config.autoCreate){ // xtype is available if this is called from factory
40265 this.el = Roo.get(el);
40266 if(!this.el && config && config.autoCreate){
40267 if(typeof config.autoCreate == "object"){
40268 if(!config.autoCreate.id){
40269 config.autoCreate.id = config.id||el;
40271 this.el = Roo.DomHelper.append(document.body,
40272 config.autoCreate, true);
40276 cls: (config.cls || '') +
40277 (config.background ? ' bg-' + config.background : '') +
40278 " roo-layout-inactive-content",
40281 if (config.iframe) {
40285 style : 'border: 0px',
40286 src : 'about:blank'
40292 elcfg.html = config.html;
40296 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40297 if (config.iframe) {
40298 this.iframeEl = this.el.select('iframe',true).first();
40303 this.closable = false;
40304 this.loaded = false;
40305 this.active = false;
40308 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40310 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40312 this.wrapEl = this.el; //this.el.wrap();
40314 if (config.toolbar.items) {
40315 ti = config.toolbar.items ;
40316 delete config.toolbar.items ;
40320 this.toolbar.render(this.wrapEl, 'before');
40321 for(var i =0;i < ti.length;i++) {
40322 // Roo.log(['add child', items[i]]);
40323 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40325 this.toolbar.items = nitems;
40326 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40327 delete config.toolbar;
40331 // xtype created footer. - not sure if will work as we normally have to render first..
40332 if (this.footer && !this.footer.el && this.footer.xtype) {
40333 if (!this.wrapEl) {
40334 this.wrapEl = this.el.wrap();
40337 this.footer.container = this.wrapEl.createChild();
40339 this.footer = Roo.factory(this.footer, Roo);
40344 if(typeof config == "string"){
40345 this.title = config;
40347 Roo.apply(this, config);
40351 this.resizeEl = Roo.get(this.resizeEl, true);
40353 this.resizeEl = this.el;
40355 // handle view.xtype
40363 * Fires when this panel is activated.
40364 * @param {Roo.ContentPanel} this
40368 * @event deactivate
40369 * Fires when this panel is activated.
40370 * @param {Roo.ContentPanel} this
40372 "deactivate" : true,
40376 * Fires when this panel is resized if fitToFrame is true.
40377 * @param {Roo.ContentPanel} this
40378 * @param {Number} width The width after any component adjustments
40379 * @param {Number} height The height after any component adjustments
40385 * Fires when this tab is created
40386 * @param {Roo.ContentPanel} this
40392 * Fires when this content is scrolled
40393 * @param {Roo.ContentPanel} this
40394 * @param {Event} scrollEvent
40405 if(this.autoScroll && !this.iframe){
40406 this.resizeEl.setStyle("overflow", "auto");
40407 this.resizeEl.on('scroll', this.onScroll, this);
40409 // fix randome scrolling
40410 //this.el.on('scroll', function() {
40411 // Roo.log('fix random scolling');
40412 // this.scrollTo('top',0);
40415 content = content || this.content;
40417 this.setContent(content);
40419 if(config && config.url){
40420 this.setUrl(this.url, this.params, this.loadOnce);
40425 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40427 if (this.view && typeof(this.view.xtype) != 'undefined') {
40428 this.view.el = this.el.appendChild(document.createElement("div"));
40429 this.view = Roo.factory(this.view);
40430 this.view.render && this.view.render(false, '');
40434 this.fireEvent('render', this);
40437 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40447 /* Resize Element - use this to work out scroll etc. */
40450 setRegion : function(region){
40451 this.region = region;
40452 this.setActiveClass(region && !this.background);
40456 setActiveClass: function(state)
40459 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40460 this.el.setStyle('position','relative');
40462 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40463 this.el.setStyle('position', 'absolute');
40468 * Returns the toolbar for this Panel if one was configured.
40469 * @return {Roo.Toolbar}
40471 getToolbar : function(){
40472 return this.toolbar;
40475 setActiveState : function(active)
40477 this.active = active;
40478 this.setActiveClass(active);
40480 if(this.fireEvent("deactivate", this) === false){
40485 this.fireEvent("activate", this);
40489 * Updates this panel's element (not for iframe)
40490 * @param {String} content The new content
40491 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40493 setContent : function(content, loadScripts){
40498 this.el.update(content, loadScripts);
40501 ignoreResize : function(w, h){
40502 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40505 this.lastSize = {width: w, height: h};
40510 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40511 * @return {Roo.UpdateManager} The UpdateManager
40513 getUpdateManager : function(){
40517 return this.el.getUpdateManager();
40520 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40521 * Does not work with IFRAME contents
40522 * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
40525 url: "your-url.php",
40526 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40527 callback: yourFunction,
40528 scope: yourObject, //(optional scope)
40531 text: "Loading...",
40537 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40538 * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
40539 * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
40540 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40541 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
40542 * @return {Roo.ContentPanel} this
40550 var um = this.el.getUpdateManager();
40551 um.update.apply(um, arguments);
40557 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
40558 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40559 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
40560 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
40561 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40563 setUrl : function(url, params, loadOnce){
40565 this.iframeEl.dom.src = url;
40569 if(this.refreshDelegate){
40570 this.removeListener("activate", this.refreshDelegate);
40572 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40573 this.on("activate", this.refreshDelegate);
40574 return this.el.getUpdateManager();
40577 _handleRefresh : function(url, params, loadOnce){
40578 if(!loadOnce || !this.loaded){
40579 var updater = this.el.getUpdateManager();
40580 updater.update(url, params, this._setLoaded.createDelegate(this));
40584 _setLoaded : function(){
40585 this.loaded = true;
40589 * Returns this panel's id
40592 getId : function(){
40597 * Returns this panel's element - used by regiosn to add.
40598 * @return {Roo.Element}
40600 getEl : function(){
40601 return this.wrapEl || this.el;
40606 adjustForComponents : function(width, height)
40608 //Roo.log('adjustForComponents ');
40609 if(this.resizeEl != this.el){
40610 width -= this.el.getFrameWidth('lr');
40611 height -= this.el.getFrameWidth('tb');
40614 var te = this.toolbar.getEl();
40615 te.setWidth(width);
40616 height -= te.getHeight();
40619 var te = this.footer.getEl();
40620 te.setWidth(width);
40621 height -= te.getHeight();
40625 if(this.adjustments){
40626 width += this.adjustments[0];
40627 height += this.adjustments[1];
40629 return {"width": width, "height": height};
40632 setSize : function(width, height){
40633 if(this.fitToFrame && !this.ignoreResize(width, height)){
40634 if(this.fitContainer && this.resizeEl != this.el){
40635 this.el.setSize(width, height);
40637 var size = this.adjustForComponents(width, height);
40639 this.iframeEl.setSize(width,height);
40642 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40643 this.fireEvent('resize', this, size.width, size.height);
40650 * Returns this panel's title
40653 getTitle : function(){
40655 if (typeof(this.title) != 'object') {
40660 for (var k in this.title) {
40661 if (!this.title.hasOwnProperty(k)) {
40665 if (k.indexOf('-') >= 0) {
40666 var s = k.split('-');
40667 for (var i = 0; i<s.length; i++) {
40668 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40671 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40678 * Set this panel's title
40679 * @param {String} title
40681 setTitle : function(title){
40682 this.title = title;
40684 this.region.updatePanelTitle(this, title);
40689 * Returns true is this panel was configured to be closable
40690 * @return {Boolean}
40692 isClosable : function(){
40693 return this.closable;
40696 beforeSlide : function(){
40698 this.resizeEl.clip();
40701 afterSlide : function(){
40703 this.resizeEl.unclip();
40707 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40708 * Will fail silently if the {@link #setUrl} method has not been called.
40709 * This does not activate the panel, just updates its content.
40711 refresh : function(){
40712 if(this.refreshDelegate){
40713 this.loaded = false;
40714 this.refreshDelegate();
40719 * Destroys this panel
40721 destroy : function(){
40722 this.el.removeAllListeners();
40723 var tempEl = document.createElement("span");
40724 tempEl.appendChild(this.el.dom);
40725 tempEl.innerHTML = "";
40731 * form - if the content panel contains a form - this is a reference to it.
40732 * @type {Roo.form.Form}
40736 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40737 * This contains a reference to it.
40743 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40753 * @param {Object} cfg Xtype definition of item to add.
40757 getChildContainer: function () {
40758 return this.getEl();
40762 onScroll : function(e)
40764 this.fireEvent('scroll', this, e);
40769 var ret = new Roo.factory(cfg);
40774 if (cfg.xtype.match(/^Form$/)) {
40777 //if (this.footer) {
40778 // el = this.footer.container.insertSibling(false, 'before');
40780 el = this.el.createChild();
40783 this.form = new Roo.form.Form(cfg);
40786 if ( this.form.allItems.length) {
40787 this.form.render(el.dom);
40791 // should only have one of theses..
40792 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40793 // views.. should not be just added - used named prop 'view''
40795 cfg.el = this.el.appendChild(document.createElement("div"));
40798 var ret = new Roo.factory(cfg);
40800 ret.render && ret.render(false, ''); // render blank..
40810 * @class Roo.bootstrap.panel.Grid
40811 * @extends Roo.bootstrap.panel.Content
40813 * Create a new GridPanel.
40814 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40815 * @param {Object} config A the config object
40821 Roo.bootstrap.panel.Grid = function(config)
40825 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40826 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40828 config.el = this.wrapper;
40829 //this.el = this.wrapper;
40831 if (config.container) {
40832 // ctor'ed from a Border/panel.grid
40835 this.wrapper.setStyle("overflow", "hidden");
40836 this.wrapper.addClass('roo-grid-container');
40841 if(config.toolbar){
40842 var tool_el = this.wrapper.createChild();
40843 this.toolbar = Roo.factory(config.toolbar);
40845 if (config.toolbar.items) {
40846 ti = config.toolbar.items ;
40847 delete config.toolbar.items ;
40851 this.toolbar.render(tool_el);
40852 for(var i =0;i < ti.length;i++) {
40853 // Roo.log(['add child', items[i]]);
40854 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40856 this.toolbar.items = nitems;
40858 delete config.toolbar;
40861 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40862 config.grid.scrollBody = true;;
40863 config.grid.monitorWindowResize = false; // turn off autosizing
40864 config.grid.autoHeight = false;
40865 config.grid.autoWidth = false;
40867 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40869 if (config.background) {
40870 // render grid on panel activation (if panel background)
40871 this.on('activate', function(gp) {
40872 if (!gp.grid.rendered) {
40873 gp.grid.render(this.wrapper);
40874 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40879 this.grid.render(this.wrapper);
40880 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40883 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40884 // ??? needed ??? config.el = this.wrapper;
40889 // xtype created footer. - not sure if will work as we normally have to render first..
40890 if (this.footer && !this.footer.el && this.footer.xtype) {
40892 var ctr = this.grid.getView().getFooterPanel(true);
40893 this.footer.dataSource = this.grid.dataSource;
40894 this.footer = Roo.factory(this.footer, Roo);
40895 this.footer.render(ctr);
40905 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40906 getId : function(){
40907 return this.grid.id;
40911 * Returns the grid for this panel
40912 * @return {Roo.bootstrap.Table}
40914 getGrid : function(){
40918 setSize : function(width, height){
40919 if(!this.ignoreResize(width, height)){
40920 var grid = this.grid;
40921 var size = this.adjustForComponents(width, height);
40922 // tfoot is not a footer?
40925 var gridel = grid.getGridEl();
40926 gridel.setSize(size.width, size.height);
40928 var tbd = grid.getGridEl().select('tbody', true).first();
40929 var thd = grid.getGridEl().select('thead',true).first();
40930 var tbf= grid.getGridEl().select('tfoot', true).first();
40933 size.height -= tbf.getHeight();
40936 size.height -= thd.getHeight();
40939 tbd.setSize(size.width, size.height );
40940 // this is for the account management tab -seems to work there.
40941 var thd = grid.getGridEl().select('thead',true).first();
40943 // tbd.setSize(size.width, size.height - thd.getHeight());
40952 beforeSlide : function(){
40953 this.grid.getView().scroller.clip();
40956 afterSlide : function(){
40957 this.grid.getView().scroller.unclip();
40960 destroy : function(){
40961 this.grid.destroy();
40963 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40968 * @class Roo.bootstrap.panel.Nest
40969 * @extends Roo.bootstrap.panel.Content
40971 * Create a new Panel, that can contain a layout.Border.
40974 * @param {Roo.BorderLayout} layout The layout for this panel
40975 * @param {String/Object} config A string to set only the title or a config object
40977 Roo.bootstrap.panel.Nest = function(config)
40979 // construct with only one argument..
40980 /* FIXME - implement nicer consturctors
40981 if (layout.layout) {
40983 layout = config.layout;
40984 delete config.layout;
40986 if (layout.xtype && !layout.getEl) {
40987 // then layout needs constructing..
40988 layout = Roo.factory(layout, Roo);
40992 config.el = config.layout.getEl();
40994 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40996 config.layout.monitorWindowResize = false; // turn off autosizing
40997 this.layout = config.layout;
40998 this.layout.getEl().addClass("roo-layout-nested-layout");
40999 this.layout.parent = this;
41006 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41008 setSize : function(width, height){
41009 if(!this.ignoreResize(width, height)){
41010 var size = this.adjustForComponents(width, height);
41011 var el = this.layout.getEl();
41012 if (size.height < 1) {
41013 el.setWidth(size.width);
41015 el.setSize(size.width, size.height);
41017 var touch = el.dom.offsetWidth;
41018 this.layout.layout();
41019 // ie requires a double layout on the first pass
41020 if(Roo.isIE && !this.initialized){
41021 this.initialized = true;
41022 this.layout.layout();
41027 // activate all subpanels if not currently active..
41029 setActiveState : function(active){
41030 this.active = active;
41031 this.setActiveClass(active);
41034 this.fireEvent("deactivate", this);
41038 this.fireEvent("activate", this);
41039 // not sure if this should happen before or after..
41040 if (!this.layout) {
41041 return; // should not happen..
41044 for (var r in this.layout.regions) {
41045 reg = this.layout.getRegion(r);
41046 if (reg.getActivePanel()) {
41047 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41048 reg.setActivePanel(reg.getActivePanel());
41051 if (!reg.panels.length) {
41054 reg.showPanel(reg.getPanel(0));
41063 * Returns the nested BorderLayout for this panel
41064 * @return {Roo.BorderLayout}
41066 getLayout : function(){
41067 return this.layout;
41071 * Adds a xtype elements to the layout of the nested panel
41075 xtype : 'ContentPanel',
41082 xtype : 'NestedLayoutPanel',
41088 items : [ ... list of content panels or nested layout panels.. ]
41092 * @param {Object} cfg Xtype definition of item to add.
41094 addxtype : function(cfg) {
41095 return this.layout.addxtype(cfg);
41100 * Ext JS Library 1.1.1
41101 * Copyright(c) 2006-2007, Ext JS, LLC.
41103 * Originally Released Under LGPL - original licence link has changed is not relivant.
41106 * <script type="text/javascript">
41109 * @class Roo.TabPanel
41110 * @extends Roo.util.Observable
41111 * A lightweight tab container.
41115 // basic tabs 1, built from existing content
41116 var tabs = new Roo.TabPanel("tabs1");
41117 tabs.addTab("script", "View Script");
41118 tabs.addTab("markup", "View Markup");
41119 tabs.activate("script");
41121 // more advanced tabs, built from javascript
41122 var jtabs = new Roo.TabPanel("jtabs");
41123 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41125 // set up the UpdateManager
41126 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41127 var updater = tab2.getUpdateManager();
41128 updater.setDefaultUrl("ajax1.htm");
41129 tab2.on('activate', updater.refresh, updater, true);
41131 // Use setUrl for Ajax loading
41132 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41133 tab3.setUrl("ajax2.htm", null, true);
41136 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41139 jtabs.activate("jtabs-1");
41142 * Create a new TabPanel.
41143 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41144 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41146 Roo.bootstrap.panel.Tabs = function(config){
41148 * The container element for this TabPanel.
41149 * @type Roo.Element
41151 this.el = Roo.get(config.el);
41154 if(typeof config == "boolean"){
41155 this.tabPosition = config ? "bottom" : "top";
41157 Roo.apply(this, config);
41161 if(this.tabPosition == "bottom"){
41162 // if tabs are at the bottom = create the body first.
41163 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41164 this.el.addClass("roo-tabs-bottom");
41166 // next create the tabs holders
41168 if (this.tabPosition == "west"){
41170 var reg = this.region; // fake it..
41172 if (!reg.mgr.parent) {
41175 reg = reg.mgr.parent.region;
41177 Roo.log("got nest?");
41179 if (reg.mgr.getRegion('west')) {
41180 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41181 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41182 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41183 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41184 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41192 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41193 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41194 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41195 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41200 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41203 // finally - if tabs are at the top, then create the body last..
41204 if(this.tabPosition != "bottom"){
41205 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41206 * @type Roo.Element
41208 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41209 this.el.addClass("roo-tabs-top");
41213 this.bodyEl.setStyle("position", "relative");
41215 this.active = null;
41216 this.activateDelegate = this.activate.createDelegate(this);
41221 * Fires when the active tab changes
41222 * @param {Roo.TabPanel} this
41223 * @param {Roo.TabPanelItem} activePanel The new active tab
41227 * @event beforetabchange
41228 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41229 * @param {Roo.TabPanel} this
41230 * @param {Object} e Set cancel to true on this object to cancel the tab change
41231 * @param {Roo.TabPanelItem} tab The tab being changed to
41233 "beforetabchange" : true
41236 Roo.EventManager.onWindowResize(this.onResize, this);
41237 this.cpad = this.el.getPadding("lr");
41238 this.hiddenCount = 0;
41241 // toolbar on the tabbar support...
41242 if (this.toolbar) {
41243 alert("no toolbar support yet");
41244 this.toolbar = false;
41246 var tcfg = this.toolbar;
41247 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41248 this.toolbar = new Roo.Toolbar(tcfg);
41249 if (Roo.isSafari) {
41250 var tbl = tcfg.container.child('table', true);
41251 tbl.setAttribute('width', '100%');
41259 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41262 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41264 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41266 tabPosition : "top",
41268 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41270 currentTabWidth : 0,
41272 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41276 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41280 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41282 preferredTabWidth : 175,
41284 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41286 resizeTabs : false,
41288 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41290 monitorResize : true,
41292 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41294 toolbar : false, // set by caller..
41296 region : false, /// set by caller
41298 disableTooltips : true, // not used yet...
41301 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41302 * @param {String} id The id of the div to use <b>or create</b>
41303 * @param {String} text The text for the tab
41304 * @param {String} content (optional) Content to put in the TabPanelItem body
41305 * @param {Boolean} closable (optional) True to create a close icon on the tab
41306 * @return {Roo.TabPanelItem} The created TabPanelItem
41308 addTab : function(id, text, content, closable, tpl)
41310 var item = new Roo.bootstrap.panel.TabItem({
41314 closable : closable,
41317 this.addTabItem(item);
41319 item.setContent(content);
41325 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41326 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41327 * @return {Roo.TabPanelItem}
41329 getTab : function(id){
41330 return this.items[id];
41334 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41335 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41337 hideTab : function(id){
41338 var t = this.items[id];
41341 this.hiddenCount++;
41342 this.autoSizeTabs();
41347 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41348 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41350 unhideTab : function(id){
41351 var t = this.items[id];
41353 t.setHidden(false);
41354 this.hiddenCount--;
41355 this.autoSizeTabs();
41360 * Adds an existing {@link Roo.TabPanelItem}.
41361 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41363 addTabItem : function(item)
41365 this.items[item.id] = item;
41366 this.items.push(item);
41367 this.autoSizeTabs();
41368 // if(this.resizeTabs){
41369 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41370 // this.autoSizeTabs();
41372 // item.autoSize();
41377 * Removes a {@link Roo.TabPanelItem}.
41378 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41380 removeTab : function(id){
41381 var items = this.items;
41382 var tab = items[id];
41383 if(!tab) { return; }
41384 var index = items.indexOf(tab);
41385 if(this.active == tab && items.length > 1){
41386 var newTab = this.getNextAvailable(index);
41391 this.stripEl.dom.removeChild(tab.pnode.dom);
41392 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41393 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41395 items.splice(index, 1);
41396 delete this.items[tab.id];
41397 tab.fireEvent("close", tab);
41398 tab.purgeListeners();
41399 this.autoSizeTabs();
41402 getNextAvailable : function(start){
41403 var items = this.items;
41405 // look for a next tab that will slide over to
41406 // replace the one being removed
41407 while(index < items.length){
41408 var item = items[++index];
41409 if(item && !item.isHidden()){
41413 // if one isn't found select the previous tab (on the left)
41416 var item = items[--index];
41417 if(item && !item.isHidden()){
41425 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41426 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41428 disableTab : function(id){
41429 var tab = this.items[id];
41430 if(tab && this.active != tab){
41436 * Enables a {@link Roo.TabPanelItem} that is disabled.
41437 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41439 enableTab : function(id){
41440 var tab = this.items[id];
41445 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41446 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41447 * @return {Roo.TabPanelItem} The TabPanelItem.
41449 activate : function(id)
41451 //Roo.log('activite:' + id);
41453 var tab = this.items[id];
41457 if(tab == this.active || tab.disabled){
41461 this.fireEvent("beforetabchange", this, e, tab);
41462 if(e.cancel !== true && !tab.disabled){
41464 this.active.hide();
41466 this.active = this.items[id];
41467 this.active.show();
41468 this.fireEvent("tabchange", this, this.active);
41474 * Gets the active {@link Roo.TabPanelItem}.
41475 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41477 getActiveTab : function(){
41478 return this.active;
41482 * Updates the tab body element to fit the height of the container element
41483 * for overflow scrolling
41484 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41486 syncHeight : function(targetHeight){
41487 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41488 var bm = this.bodyEl.getMargins();
41489 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41490 this.bodyEl.setHeight(newHeight);
41494 onResize : function(){
41495 if(this.monitorResize){
41496 this.autoSizeTabs();
41501 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41503 beginUpdate : function(){
41504 this.updating = true;
41508 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41510 endUpdate : function(){
41511 this.updating = false;
41512 this.autoSizeTabs();
41516 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41518 autoSizeTabs : function()
41520 var count = this.items.length;
41521 var vcount = count - this.hiddenCount;
41524 this.stripEl.hide();
41526 this.stripEl.show();
41529 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41534 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41535 var availWidth = Math.floor(w / vcount);
41536 var b = this.stripBody;
41537 if(b.getWidth() > w){
41538 var tabs = this.items;
41539 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41540 if(availWidth < this.minTabWidth){
41541 /*if(!this.sleft){ // incomplete scrolling code
41542 this.createScrollButtons();
41545 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41548 if(this.currentTabWidth < this.preferredTabWidth){
41549 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41555 * Returns the number of tabs in this TabPanel.
41558 getCount : function(){
41559 return this.items.length;
41563 * Resizes all the tabs to the passed width
41564 * @param {Number} The new width
41566 setTabWidth : function(width){
41567 this.currentTabWidth = width;
41568 for(var i = 0, len = this.items.length; i < len; i++) {
41569 if(!this.items[i].isHidden()) {
41570 this.items[i].setWidth(width);
41576 * Destroys this TabPanel
41577 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41579 destroy : function(removeEl){
41580 Roo.EventManager.removeResizeListener(this.onResize, this);
41581 for(var i = 0, len = this.items.length; i < len; i++){
41582 this.items[i].purgeListeners();
41584 if(removeEl === true){
41585 this.el.update("");
41590 createStrip : function(container)
41592 var strip = document.createElement("nav");
41593 strip.className = Roo.bootstrap.version == 4 ?
41594 "navbar-light bg-light" :
41595 "navbar navbar-default"; //"x-tabs-wrap";
41596 container.appendChild(strip);
41600 createStripList : function(strip)
41602 // div wrapper for retard IE
41603 // returns the "tr" element.
41604 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41605 //'<div class="x-tabs-strip-wrap">'+
41606 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41607 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41608 return strip.firstChild; //.firstChild.firstChild.firstChild;
41610 createBody : function(container)
41612 var body = document.createElement("div");
41613 Roo.id(body, "tab-body");
41614 //Roo.fly(body).addClass("x-tabs-body");
41615 Roo.fly(body).addClass("tab-content");
41616 container.appendChild(body);
41619 createItemBody :function(bodyEl, id){
41620 var body = Roo.getDom(id);
41622 body = document.createElement("div");
41625 //Roo.fly(body).addClass("x-tabs-item-body");
41626 Roo.fly(body).addClass("tab-pane");
41627 bodyEl.insertBefore(body, bodyEl.firstChild);
41631 createStripElements : function(stripEl, text, closable, tpl)
41633 var td = document.createElement("li"); // was td..
41634 td.className = 'nav-item';
41636 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41639 stripEl.appendChild(td);
41641 td.className = "x-tabs-closable";
41642 if(!this.closeTpl){
41643 this.closeTpl = new Roo.Template(
41644 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41645 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41646 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41649 var el = this.closeTpl.overwrite(td, {"text": text});
41650 var close = el.getElementsByTagName("div")[0];
41651 var inner = el.getElementsByTagName("em")[0];
41652 return {"el": el, "close": close, "inner": inner};
41655 // not sure what this is..
41656 // if(!this.tabTpl){
41657 //this.tabTpl = new Roo.Template(
41658 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41659 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41661 // this.tabTpl = new Roo.Template(
41662 // '<a href="#">' +
41663 // '<span unselectable="on"' +
41664 // (this.disableTooltips ? '' : ' title="{text}"') +
41665 // ' >{text}</span></a>'
41671 var template = tpl || this.tabTpl || false;
41674 template = new Roo.Template(
41675 Roo.bootstrap.version == 4 ?
41677 '<a class="nav-link" href="#" unselectable="on"' +
41678 (this.disableTooltips ? '' : ' title="{text}"') +
41681 '<a class="nav-link" href="#">' +
41682 '<span unselectable="on"' +
41683 (this.disableTooltips ? '' : ' title="{text}"') +
41684 ' >{text}</span></a>'
41689 switch (typeof(template)) {
41693 template = new Roo.Template(template);
41699 var el = template.overwrite(td, {"text": text});
41701 var inner = el.getElementsByTagName("span")[0];
41703 return {"el": el, "inner": inner};
41711 * @class Roo.TabPanelItem
41712 * @extends Roo.util.Observable
41713 * Represents an individual item (tab plus body) in a TabPanel.
41714 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41715 * @param {String} id The id of this TabPanelItem
41716 * @param {String} text The text for the tab of this TabPanelItem
41717 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41719 Roo.bootstrap.panel.TabItem = function(config){
41721 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41722 * @type Roo.TabPanel
41724 this.tabPanel = config.panel;
41726 * The id for this TabPanelItem
41729 this.id = config.id;
41731 this.disabled = false;
41733 this.text = config.text;
41735 this.loaded = false;
41736 this.closable = config.closable;
41739 * The body element for this TabPanelItem.
41740 * @type Roo.Element
41742 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41743 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41744 this.bodyEl.setStyle("display", "block");
41745 this.bodyEl.setStyle("zoom", "1");
41746 //this.hideAction();
41748 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41750 this.el = Roo.get(els.el);
41751 this.inner = Roo.get(els.inner, true);
41752 this.textEl = Roo.bootstrap.version == 4 ?
41753 this.el : Roo.get(this.el.dom.firstChild, true);
41755 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41756 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41759 // this.el.on("mousedown", this.onTabMouseDown, this);
41760 this.el.on("click", this.onTabClick, this);
41762 if(config.closable){
41763 var c = Roo.get(els.close, true);
41764 c.dom.title = this.closeText;
41765 c.addClassOnOver("close-over");
41766 c.on("click", this.closeClick, this);
41772 * Fires when this tab becomes the active tab.
41773 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41774 * @param {Roo.TabPanelItem} this
41778 * @event beforeclose
41779 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41780 * @param {Roo.TabPanelItem} this
41781 * @param {Object} e Set cancel to true on this object to cancel the close.
41783 "beforeclose": true,
41786 * Fires when this tab is closed.
41787 * @param {Roo.TabPanelItem} this
41791 * @event deactivate
41792 * Fires when this tab is no longer the active tab.
41793 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41794 * @param {Roo.TabPanelItem} this
41796 "deactivate" : true
41798 this.hidden = false;
41800 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41805 purgeListeners : function(){
41806 Roo.util.Observable.prototype.purgeListeners.call(this);
41807 this.el.removeAllListeners();
41810 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41813 this.status_node.addClass("active");
41816 this.tabPanel.stripWrap.repaint();
41818 this.fireEvent("activate", this.tabPanel, this);
41822 * Returns true if this tab is the active tab.
41823 * @return {Boolean}
41825 isActive : function(){
41826 return this.tabPanel.getActiveTab() == this;
41830 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41833 this.status_node.removeClass("active");
41835 this.fireEvent("deactivate", this.tabPanel, this);
41838 hideAction : function(){
41839 this.bodyEl.hide();
41840 this.bodyEl.setStyle("position", "absolute");
41841 this.bodyEl.setLeft("-20000px");
41842 this.bodyEl.setTop("-20000px");
41845 showAction : function(){
41846 this.bodyEl.setStyle("position", "relative");
41847 this.bodyEl.setTop("");
41848 this.bodyEl.setLeft("");
41849 this.bodyEl.show();
41853 * Set the tooltip for the tab.
41854 * @param {String} tooltip The tab's tooltip
41856 setTooltip : function(text){
41857 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41858 this.textEl.dom.qtip = text;
41859 this.textEl.dom.removeAttribute('title');
41861 this.textEl.dom.title = text;
41865 onTabClick : function(e){
41866 e.preventDefault();
41867 this.tabPanel.activate(this.id);
41870 onTabMouseDown : function(e){
41871 e.preventDefault();
41872 this.tabPanel.activate(this.id);
41875 getWidth : function(){
41876 return this.inner.getWidth();
41879 setWidth : function(width){
41880 var iwidth = width - this.linode.getPadding("lr");
41881 this.inner.setWidth(iwidth);
41882 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41883 this.linode.setWidth(width);
41887 * Show or hide the tab
41888 * @param {Boolean} hidden True to hide or false to show.
41890 setHidden : function(hidden){
41891 this.hidden = hidden;
41892 this.linode.setStyle("display", hidden ? "none" : "");
41896 * Returns true if this tab is "hidden"
41897 * @return {Boolean}
41899 isHidden : function(){
41900 return this.hidden;
41904 * Returns the text for this tab
41907 getText : function(){
41911 autoSize : function(){
41912 //this.el.beginMeasure();
41913 this.textEl.setWidth(1);
41915 * #2804 [new] Tabs in Roojs
41916 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41918 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41919 //this.el.endMeasure();
41923 * Sets the text for the tab (Note: this also sets the tooltip text)
41924 * @param {String} text The tab's text and tooltip
41926 setText : function(text){
41928 this.textEl.update(text);
41929 this.setTooltip(text);
41930 //if(!this.tabPanel.resizeTabs){
41931 // this.autoSize();
41935 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41937 activate : function(){
41938 this.tabPanel.activate(this.id);
41942 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41944 disable : function(){
41945 if(this.tabPanel.active != this){
41946 this.disabled = true;
41947 this.status_node.addClass("disabled");
41952 * Enables this TabPanelItem if it was previously disabled.
41954 enable : function(){
41955 this.disabled = false;
41956 this.status_node.removeClass("disabled");
41960 * Sets the content for this TabPanelItem.
41961 * @param {String} content The content
41962 * @param {Boolean} loadScripts true to look for and load scripts
41964 setContent : function(content, loadScripts){
41965 this.bodyEl.update(content, loadScripts);
41969 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41970 * @return {Roo.UpdateManager} The UpdateManager
41972 getUpdateManager : function(){
41973 return this.bodyEl.getUpdateManager();
41977 * Set a URL to be used to load the content for this TabPanelItem.
41978 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41979 * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
41980 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
41981 * @return {Roo.UpdateManager} The UpdateManager
41983 setUrl : function(url, params, loadOnce){
41984 if(this.refreshDelegate){
41985 this.un('activate', this.refreshDelegate);
41987 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41988 this.on("activate", this.refreshDelegate);
41989 return this.bodyEl.getUpdateManager();
41993 _handleRefresh : function(url, params, loadOnce){
41994 if(!loadOnce || !this.loaded){
41995 var updater = this.bodyEl.getUpdateManager();
41996 updater.update(url, params, this._setLoaded.createDelegate(this));
42001 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42002 * Will fail silently if the setUrl method has not been called.
42003 * This does not activate the panel, just updates its content.
42005 refresh : function(){
42006 if(this.refreshDelegate){
42007 this.loaded = false;
42008 this.refreshDelegate();
42013 _setLoaded : function(){
42014 this.loaded = true;
42018 closeClick : function(e){
42021 this.fireEvent("beforeclose", this, o);
42022 if(o.cancel !== true){
42023 this.tabPanel.removeTab(this.id);
42027 * The text displayed in the tooltip for the close icon.
42030 closeText : "Close this tab"
42033 * This script refer to:
42034 * Title: International Telephone Input
42035 * Author: Jack O'Connor
42036 * Code version: v12.1.12
42037 * Availability: https://github.com/jackocnr/intl-tel-input.git
42040 Roo.bootstrap.PhoneInputData = function() {
42043 "Afghanistan (افغانستان)",
42048 "Albania (Shqipëri)",
42053 "Algeria (الجزائر)",
42078 "Antigua and Barbuda",
42088 "Armenia (Հայաստան)",
42104 "Austria (Österreich)",
42109 "Azerbaijan (Azərbaycan)",
42119 "Bahrain (البحرين)",
42124 "Bangladesh (বাংলাদেশ)",
42134 "Belarus (Беларусь)",
42139 "Belgium (België)",
42169 "Bosnia and Herzegovina (Босна и Херцеговина)",
42184 "British Indian Ocean Territory",
42189 "British Virgin Islands",
42199 "Bulgaria (България)",
42209 "Burundi (Uburundi)",
42214 "Cambodia (កម្ពុជា)",
42219 "Cameroon (Cameroun)",
42228 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
42231 "Cape Verde (Kabu Verdi)",
42236 "Caribbean Netherlands",
42247 "Central African Republic (République centrafricaine)",
42267 "Christmas Island",
42273 "Cocos (Keeling) Islands",
42284 "Comoros (جزر القمر)",
42289 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42294 "Congo (Republic) (Congo-Brazzaville)",
42314 "Croatia (Hrvatska)",
42335 "Czech Republic (Česká republika)",
42340 "Denmark (Danmark)",
42355 "Dominican Republic (República Dominicana)",
42359 ["809", "829", "849"]
42377 "Equatorial Guinea (Guinea Ecuatorial)",
42397 "Falkland Islands (Islas Malvinas)",
42402 "Faroe Islands (Føroyar)",
42423 "French Guiana (Guyane française)",
42428 "French Polynesia (Polynésie française)",
42443 "Georgia (საქართველო)",
42448 "Germany (Deutschland)",
42468 "Greenland (Kalaallit Nunaat)",
42505 "Guinea-Bissau (Guiné Bissau)",
42530 "Hungary (Magyarország)",
42535 "Iceland (Ísland)",
42555 "Iraq (العراق)",
42571 "Israel (ישראל)",
42598 "Jordan (الأردن)",
42603 "Kazakhstan (Казахстан)",
42624 "Kuwait (الكويت)",
42629 "Kyrgyzstan (Кыргызстан)",
42639 "Latvia (Latvija)",
42644 "Lebanon (لبنان)",
42659 "Libya (ليبيا)",
42669 "Lithuania (Lietuva)",
42684 "Macedonia (FYROM) (Македонија)",
42689 "Madagascar (Madagasikara)",
42719 "Marshall Islands",
42729 "Mauritania (موريتانيا)",
42734 "Mauritius (Moris)",
42755 "Moldova (Republica Moldova)",
42765 "Mongolia (Монгол)",
42770 "Montenegro (Crna Gora)",
42780 "Morocco (المغرب)",
42786 "Mozambique (Moçambique)",
42791 "Myanmar (Burma) (မြန်မာ)",
42796 "Namibia (Namibië)",
42811 "Netherlands (Nederland)",
42816 "New Caledonia (Nouvelle-Calédonie)",
42851 "North Korea (조선 민주주의 인민 공화국)",
42856 "Northern Mariana Islands",
42872 "Pakistan (پاکستان)",
42882 "Palestine (فلسطين)",
42892 "Papua New Guinea",
42934 "Réunion (La Réunion)",
42940 "Romania (România)",
42956 "Saint Barthélemy",
42967 "Saint Kitts and Nevis",
42977 "Saint Martin (Saint-Martin (partie française))",
42983 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42988 "Saint Vincent and the Grenadines",
43003 "São Tomé and Príncipe (São Tomé e Príncipe)",
43008 "Saudi Arabia (المملكة العربية السعودية)",
43013 "Senegal (Sénégal)",
43043 "Slovakia (Slovensko)",
43048 "Slovenia (Slovenija)",
43058 "Somalia (Soomaaliya)",
43068 "South Korea (대한민국)",
43073 "South Sudan (جنوب السودان)",
43083 "Sri Lanka (ශ්රී ලංකාව)",
43088 "Sudan (السودان)",
43098 "Svalbard and Jan Mayen",
43109 "Sweden (Sverige)",
43114 "Switzerland (Schweiz)",
43119 "Syria (سوريا)",
43164 "Trinidad and Tobago",
43169 "Tunisia (تونس)",
43174 "Turkey (Türkiye)",
43184 "Turks and Caicos Islands",
43194 "U.S. Virgin Islands",
43204 "Ukraine (Україна)",
43209 "United Arab Emirates (الإمارات العربية المتحدة)",
43231 "Uzbekistan (Oʻzbekiston)",
43241 "Vatican City (Città del Vaticano)",
43252 "Vietnam (Việt Nam)",
43257 "Wallis and Futuna (Wallis-et-Futuna)",
43262 "Western Sahara (الصحراء الغربية)",
43268 "Yemen (اليمن)",
43292 * This script refer to:
43293 * Title: International Telephone Input
43294 * Author: Jack O'Connor
43295 * Code version: v12.1.12
43296 * Availability: https://github.com/jackocnr/intl-tel-input.git
43300 * @class Roo.bootstrap.PhoneInput
43301 * @extends Roo.bootstrap.TriggerField
43302 * An input with International dial-code selection
43304 * @cfg {String} defaultDialCode default '+852'
43305 * @cfg {Array} preferedCountries default []
43308 * Create a new PhoneInput.
43309 * @param {Object} config Configuration options
43312 Roo.bootstrap.PhoneInput = function(config) {
43313 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43316 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43318 listWidth: undefined,
43320 selectedClass: 'active',
43322 invalidClass : "has-warning",
43324 validClass: 'has-success',
43326 allowed: '0123456789',
43331 * @cfg {String} defaultDialCode The default dial code when initializing the input
43333 defaultDialCode: '+852',
43336 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
43338 preferedCountries: false,
43340 getAutoCreate : function()
43342 var data = Roo.bootstrap.PhoneInputData();
43343 var align = this.labelAlign || this.parentLabelAlign();
43346 this.allCountries = [];
43347 this.dialCodeMapping = [];
43349 for (var i = 0; i < data.length; i++) {
43351 this.allCountries[i] = {
43355 priority: c[3] || 0,
43356 areaCodes: c[4] || null
43358 this.dialCodeMapping[c[2]] = {
43361 priority: c[3] || 0,
43362 areaCodes: c[4] || null
43374 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43375 maxlength: this.max_length,
43376 cls : 'form-control tel-input',
43377 autocomplete: 'new-password'
43380 var hiddenInput = {
43383 cls: 'hidden-tel-input'
43387 hiddenInput.name = this.name;
43390 if (this.disabled) {
43391 input.disabled = true;
43394 var flag_container = {
43411 cls: this.hasFeedback ? 'has-feedback' : '',
43417 cls: 'dial-code-holder',
43424 cls: 'roo-select2-container input-group',
43431 if (this.fieldLabel.length) {
43434 tooltip: 'This field is required'
43440 cls: 'control-label',
43446 html: this.fieldLabel
43449 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43455 if(this.indicatorpos == 'right') {
43456 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43463 if(align == 'left') {
43471 if(this.labelWidth > 12){
43472 label.style = "width: " + this.labelWidth + 'px';
43474 if(this.labelWidth < 13 && this.labelmd == 0){
43475 this.labelmd = this.labelWidth;
43477 if(this.labellg > 0){
43478 label.cls += ' col-lg-' + this.labellg;
43479 input.cls += ' col-lg-' + (12 - this.labellg);
43481 if(this.labelmd > 0){
43482 label.cls += ' col-md-' + this.labelmd;
43483 container.cls += ' col-md-' + (12 - this.labelmd);
43485 if(this.labelsm > 0){
43486 label.cls += ' col-sm-' + this.labelsm;
43487 container.cls += ' col-sm-' + (12 - this.labelsm);
43489 if(this.labelxs > 0){
43490 label.cls += ' col-xs-' + this.labelxs;
43491 container.cls += ' col-xs-' + (12 - this.labelxs);
43501 var settings = this;
43503 ['xs','sm','md','lg'].map(function(size){
43504 if (settings[size]) {
43505 cfg.cls += ' col-' + size + '-' + settings[size];
43509 this.store = new Roo.data.Store({
43510 proxy : new Roo.data.MemoryProxy({}),
43511 reader : new Roo.data.JsonReader({
43522 'name' : 'dialCode',
43526 'name' : 'priority',
43530 'name' : 'areaCodes',
43537 if(!this.preferedCountries) {
43538 this.preferedCountries = [
43545 var p = this.preferedCountries.reverse();
43548 for (var i = 0; i < p.length; i++) {
43549 for (var j = 0; j < this.allCountries.length; j++) {
43550 if(this.allCountries[j].iso2 == p[i]) {
43551 var t = this.allCountries[j];
43552 this.allCountries.splice(j,1);
43553 this.allCountries.unshift(t);
43559 this.store.proxy.data = {
43561 data: this.allCountries
43567 initEvents : function()
43570 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43572 this.indicator = this.indicatorEl();
43573 this.flag = this.flagEl();
43574 this.dialCodeHolder = this.dialCodeHolderEl();
43576 this.trigger = this.el.select('div.flag-box',true).first();
43577 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43582 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43583 _this.list.setWidth(lw);
43586 this.list.on('mouseover', this.onViewOver, this);
43587 this.list.on('mousemove', this.onViewMove, this);
43588 this.inputEl().on("keyup", this.onKeyUp, this);
43589 this.inputEl().on("keypress", this.onKeyPress, this);
43591 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43593 this.view = new Roo.View(this.list, this.tpl, {
43594 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43597 this.view.on('click', this.onViewClick, this);
43598 this.setValue(this.defaultDialCode);
43601 onTriggerClick : function(e)
43603 Roo.log('trigger click');
43608 if(this.isExpanded()){
43610 this.hasFocus = false;
43612 this.store.load({});
43613 this.hasFocus = true;
43618 isExpanded : function()
43620 return this.list.isVisible();
43623 collapse : function()
43625 if(!this.isExpanded()){
43629 Roo.get(document).un('mousedown', this.collapseIf, this);
43630 Roo.get(document).un('mousewheel', this.collapseIf, this);
43631 this.fireEvent('collapse', this);
43635 expand : function()
43639 if(this.isExpanded() || !this.hasFocus){
43643 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43644 this.list.setWidth(lw);
43647 this.restrictHeight();
43649 Roo.get(document).on('mousedown', this.collapseIf, this);
43650 Roo.get(document).on('mousewheel', this.collapseIf, this);
43652 this.fireEvent('expand', this);
43655 restrictHeight : function()
43657 this.list.alignTo(this.inputEl(), this.listAlign);
43658 this.list.alignTo(this.inputEl(), this.listAlign);
43661 onViewOver : function(e, t)
43663 if(this.inKeyMode){
43666 var item = this.view.findItemFromChild(t);
43669 var index = this.view.indexOf(item);
43670 this.select(index, false);
43675 onViewClick : function(view, doFocus, el, e)
43677 var index = this.view.getSelectedIndexes()[0];
43679 var r = this.store.getAt(index);
43682 this.onSelect(r, index);
43684 if(doFocus !== false && !this.blockFocus){
43685 this.inputEl().focus();
43689 onViewMove : function(e, t)
43691 this.inKeyMode = false;
43694 select : function(index, scrollIntoView)
43696 this.selectedIndex = index;
43697 this.view.select(index);
43698 if(scrollIntoView !== false){
43699 var el = this.view.getNode(index);
43701 this.list.scrollChildIntoView(el, false);
43706 createList : function()
43708 this.list = Roo.get(document.body).createChild({
43710 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43711 style: 'display:none'
43714 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43717 collapseIf : function(e)
43719 var in_combo = e.within(this.el);
43720 var in_list = e.within(this.list);
43721 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43723 if (in_combo || in_list || is_list) {
43729 onSelect : function(record, index)
43731 if(this.fireEvent('beforeselect', this, record, index) !== false){
43733 this.setFlagClass(record.data.iso2);
43734 this.setDialCode(record.data.dialCode);
43735 this.hasFocus = false;
43737 this.fireEvent('select', this, record, index);
43741 flagEl : function()
43743 var flag = this.el.select('div.flag',true).first();
43750 dialCodeHolderEl : function()
43752 var d = this.el.select('input.dial-code-holder',true).first();
43759 setDialCode : function(v)
43761 this.dialCodeHolder.dom.value = '+'+v;
43764 setFlagClass : function(n)
43766 this.flag.dom.className = 'flag '+n;
43769 getValue : function()
43771 var v = this.inputEl().getValue();
43772 if(this.dialCodeHolder) {
43773 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43778 setValue : function(v)
43780 var d = this.getDialCode(v);
43782 //invalid dial code
43783 if(v.length == 0 || !d || d.length == 0) {
43785 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43786 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43792 this.setFlagClass(this.dialCodeMapping[d].iso2);
43793 this.setDialCode(d);
43794 this.inputEl().dom.value = v.replace('+'+d,'');
43795 this.hiddenEl().dom.value = this.getValue();
43800 getDialCode : function(v)
43804 if (v.length == 0) {
43805 return this.dialCodeHolder.dom.value;
43809 if (v.charAt(0) != "+") {
43812 var numericChars = "";
43813 for (var i = 1; i < v.length; i++) {
43814 var c = v.charAt(i);
43817 if (this.dialCodeMapping[numericChars]) {
43818 dialCode = v.substr(1, i);
43820 if (numericChars.length == 4) {
43830 this.setValue(this.defaultDialCode);
43834 hiddenEl : function()
43836 return this.el.select('input.hidden-tel-input',true).first();
43839 // after setting val
43840 onKeyUp : function(e){
43841 this.setValue(this.getValue());
43844 onKeyPress : function(e){
43845 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43852 * @class Roo.bootstrap.MoneyField
43853 * @extends Roo.bootstrap.ComboBox
43854 * Bootstrap MoneyField class
43857 * Create a new MoneyField.
43858 * @param {Object} config Configuration options
43861 Roo.bootstrap.MoneyField = function(config) {
43863 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43867 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43870 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43872 allowDecimals : true,
43874 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43876 decimalSeparator : ".",
43878 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43880 decimalPrecision : 0,
43882 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43884 allowNegative : true,
43886 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43890 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43892 minValue : Number.NEGATIVE_INFINITY,
43894 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43896 maxValue : Number.MAX_VALUE,
43898 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43900 minText : "The minimum value for this field is {0}",
43902 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43904 maxText : "The maximum value for this field is {0}",
43906 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43907 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43909 nanText : "{0} is not a valid number",
43911 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43915 * @cfg {String} defaults currency of the MoneyField
43916 * value should be in lkey
43918 defaultCurrency : false,
43920 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43922 thousandsDelimiter : false,
43924 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43935 getAutoCreate : function()
43937 var align = this.labelAlign || this.parentLabelAlign();
43949 cls : 'form-control roo-money-amount-input',
43950 autocomplete: 'new-password'
43953 var hiddenInput = {
43957 cls: 'hidden-number-input'
43960 if(this.max_length) {
43961 input.maxlength = this.max_length;
43965 hiddenInput.name = this.name;
43968 if (this.disabled) {
43969 input.disabled = true;
43972 var clg = 12 - this.inputlg;
43973 var cmd = 12 - this.inputmd;
43974 var csm = 12 - this.inputsm;
43975 var cxs = 12 - this.inputxs;
43979 cls : 'row roo-money-field',
43983 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43987 cls: 'roo-select2-container input-group',
43991 cls : 'form-control roo-money-currency-input',
43992 autocomplete: 'new-password',
43994 name : this.currencyName
43998 cls : 'input-group-addon',
44012 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44016 cls: this.hasFeedback ? 'has-feedback' : '',
44027 if (this.fieldLabel.length) {
44030 tooltip: 'This field is required'
44036 cls: 'control-label',
44042 html: this.fieldLabel
44045 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44051 if(this.indicatorpos == 'right') {
44052 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44059 if(align == 'left') {
44067 if(this.labelWidth > 12){
44068 label.style = "width: " + this.labelWidth + 'px';
44070 if(this.labelWidth < 13 && this.labelmd == 0){
44071 this.labelmd = this.labelWidth;
44073 if(this.labellg > 0){
44074 label.cls += ' col-lg-' + this.labellg;
44075 input.cls += ' col-lg-' + (12 - this.labellg);
44077 if(this.labelmd > 0){
44078 label.cls += ' col-md-' + this.labelmd;
44079 container.cls += ' col-md-' + (12 - this.labelmd);
44081 if(this.labelsm > 0){
44082 label.cls += ' col-sm-' + this.labelsm;
44083 container.cls += ' col-sm-' + (12 - this.labelsm);
44085 if(this.labelxs > 0){
44086 label.cls += ' col-xs-' + this.labelxs;
44087 container.cls += ' col-xs-' + (12 - this.labelxs);
44098 var settings = this;
44100 ['xs','sm','md','lg'].map(function(size){
44101 if (settings[size]) {
44102 cfg.cls += ' col-' + size + '-' + settings[size];
44109 initEvents : function()
44111 this.indicator = this.indicatorEl();
44113 this.initCurrencyEvent();
44115 this.initNumberEvent();
44118 initCurrencyEvent : function()
44121 throw "can not find store for combo";
44124 this.store = Roo.factory(this.store, Roo.data);
44125 this.store.parent = this;
44129 this.triggerEl = this.el.select('.input-group-addon', true).first();
44131 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44136 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44137 _this.list.setWidth(lw);
44140 this.list.on('mouseover', this.onViewOver, this);
44141 this.list.on('mousemove', this.onViewMove, this);
44142 this.list.on('scroll', this.onViewScroll, this);
44145 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44148 this.view = new Roo.View(this.list, this.tpl, {
44149 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44152 this.view.on('click', this.onViewClick, this);
44154 this.store.on('beforeload', this.onBeforeLoad, this);
44155 this.store.on('load', this.onLoad, this);
44156 this.store.on('loadexception', this.onLoadException, this);
44158 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44159 "up" : function(e){
44160 this.inKeyMode = true;
44164 "down" : function(e){
44165 if(!this.isExpanded()){
44166 this.onTriggerClick();
44168 this.inKeyMode = true;
44173 "enter" : function(e){
44176 if(this.fireEvent("specialkey", this, e)){
44177 this.onViewClick(false);
44183 "esc" : function(e){
44187 "tab" : function(e){
44190 if(this.fireEvent("specialkey", this, e)){
44191 this.onViewClick(false);
44199 doRelay : function(foo, bar, hname){
44200 if(hname == 'down' || this.scope.isExpanded()){
44201 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44209 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44213 initNumberEvent : function(e)
44215 this.inputEl().on("keydown" , this.fireKey, this);
44216 this.inputEl().on("focus", this.onFocus, this);
44217 this.inputEl().on("blur", this.onBlur, this);
44219 this.inputEl().relayEvent('keyup', this);
44221 if(this.indicator){
44222 this.indicator.addClass('invisible');
44225 this.originalValue = this.getValue();
44227 if(this.validationEvent == 'keyup'){
44228 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44229 this.inputEl().on('keyup', this.filterValidation, this);
44231 else if(this.validationEvent !== false){
44232 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44235 if(this.selectOnFocus){
44236 this.on("focus", this.preFocus, this);
44239 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44240 this.inputEl().on("keypress", this.filterKeys, this);
44242 this.inputEl().relayEvent('keypress', this);
44245 var allowed = "0123456789";
44247 if(this.allowDecimals){
44248 allowed += this.decimalSeparator;
44251 if(this.allowNegative){
44255 if(this.thousandsDelimiter) {
44259 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44261 var keyPress = function(e){
44263 var k = e.getKey();
44265 var c = e.getCharCode();
44268 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44269 allowed.indexOf(String.fromCharCode(c)) === -1
44275 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44279 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44284 this.inputEl().on("keypress", keyPress, this);
44288 onTriggerClick : function(e)
44295 this.loadNext = false;
44297 if(this.isExpanded()){
44302 this.hasFocus = true;
44304 if(this.triggerAction == 'all') {
44305 this.doQuery(this.allQuery, true);
44309 this.doQuery(this.getRawValue());
44312 getCurrency : function()
44314 var v = this.currencyEl().getValue();
44319 restrictHeight : function()
44321 this.list.alignTo(this.currencyEl(), this.listAlign);
44322 this.list.alignTo(this.currencyEl(), this.listAlign);
44325 onViewClick : function(view, doFocus, el, e)
44327 var index = this.view.getSelectedIndexes()[0];
44329 var r = this.store.getAt(index);
44332 this.onSelect(r, index);
44336 onSelect : function(record, index){
44338 if(this.fireEvent('beforeselect', this, record, index) !== false){
44340 this.setFromCurrencyData(index > -1 ? record.data : false);
44344 this.fireEvent('select', this, record, index);
44348 setFromCurrencyData : function(o)
44352 this.lastCurrency = o;
44354 if (this.currencyField) {
44355 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44357 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44360 this.lastSelectionText = currency;
44362 //setting default currency
44363 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44364 this.setCurrency(this.defaultCurrency);
44368 this.setCurrency(currency);
44371 setFromData : function(o)
44375 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44377 this.setFromCurrencyData(c);
44382 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44384 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44387 this.setValue(value);
44391 setCurrency : function(v)
44393 this.currencyValue = v;
44396 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44401 setValue : function(v)
44403 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44409 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44411 this.inputEl().dom.value = (v == '') ? '' :
44412 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44414 if(!this.allowZero && v === '0') {
44415 this.hiddenEl().dom.value = '';
44416 this.inputEl().dom.value = '';
44423 getRawValue : function()
44425 var v = this.inputEl().getValue();
44430 getValue : function()
44432 return this.fixPrecision(this.parseValue(this.getRawValue()));
44435 parseValue : function(value)
44437 if(this.thousandsDelimiter) {
44439 r = new RegExp(",", "g");
44440 value = value.replace(r, "");
44443 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44444 return isNaN(value) ? '' : value;
44448 fixPrecision : function(value)
44450 if(this.thousandsDelimiter) {
44452 r = new RegExp(",", "g");
44453 value = value.replace(r, "");
44456 var nan = isNaN(value);
44458 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44459 return nan ? '' : value;
44461 return parseFloat(value).toFixed(this.decimalPrecision);
44464 decimalPrecisionFcn : function(v)
44466 return Math.floor(v);
44469 validateValue : function(value)
44471 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44475 var num = this.parseValue(value);
44478 this.markInvalid(String.format(this.nanText, value));
44482 if(num < this.minValue){
44483 this.markInvalid(String.format(this.minText, this.minValue));
44487 if(num > this.maxValue){
44488 this.markInvalid(String.format(this.maxText, this.maxValue));
44495 validate : function()
44497 if(this.disabled || this.allowBlank){
44502 var currency = this.getCurrency();
44504 if(this.validateValue(this.getRawValue()) && currency.length){
44509 this.markInvalid();
44513 getName: function()
44518 beforeBlur : function()
44524 var v = this.parseValue(this.getRawValue());
44531 onBlur : function()
44535 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44536 //this.el.removeClass(this.focusClass);
44539 this.hasFocus = false;
44541 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44545 var v = this.getValue();
44547 if(String(v) !== String(this.startValue)){
44548 this.fireEvent('change', this, v, this.startValue);
44551 this.fireEvent("blur", this);
44554 inputEl : function()
44556 return this.el.select('.roo-money-amount-input', true).first();
44559 currencyEl : function()
44561 return this.el.select('.roo-money-currency-input', true).first();
44564 hiddenEl : function()
44566 return this.el.select('input.hidden-number-input',true).first();
44570 * @class Roo.bootstrap.BezierSignature
44571 * @extends Roo.bootstrap.Component
44572 * Bootstrap BezierSignature class
44573 * This script refer to:
44574 * Title: Signature Pad
44576 * Availability: https://github.com/szimek/signature_pad
44579 * Create a new BezierSignature
44580 * @param {Object} config The config object
44583 Roo.bootstrap.BezierSignature = function(config){
44584 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44590 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44597 mouse_btn_down: true,
44600 * @cfg {int} canvas height
44602 canvas_height: '200px',
44605 * @cfg {float|function} Radius of a single dot.
44610 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44615 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44620 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44625 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44630 * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
44632 bg_color: 'rgba(0, 0, 0, 0)',
44635 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44637 dot_color: 'black',
44640 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44642 velocity_filter_weight: 0.7,
44645 * @cfg {function} Callback when stroke begin.
44650 * @cfg {function} Callback when stroke end.
44654 getAutoCreate : function()
44656 var cls = 'roo-signature column';
44659 cls += ' ' + this.cls;
44669 for(var i = 0; i < col_sizes.length; i++) {
44670 if(this[col_sizes[i]]) {
44671 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44681 cls: 'roo-signature-body',
44685 cls: 'roo-signature-body-canvas',
44686 height: this.canvas_height,
44687 width: this.canvas_width
44694 style: 'display: none'
44702 initEvents: function()
44704 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44706 var canvas = this.canvasEl();
44708 // mouse && touch event swapping...
44709 canvas.dom.style.touchAction = 'none';
44710 canvas.dom.style.msTouchAction = 'none';
44712 this.mouse_btn_down = false;
44713 canvas.on('mousedown', this._handleMouseDown, this);
44714 canvas.on('mousemove', this._handleMouseMove, this);
44715 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44717 if (window.PointerEvent) {
44718 canvas.on('pointerdown', this._handleMouseDown, this);
44719 canvas.on('pointermove', this._handleMouseMove, this);
44720 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44723 if ('ontouchstart' in window) {
44724 canvas.on('touchstart', this._handleTouchStart, this);
44725 canvas.on('touchmove', this._handleTouchMove, this);
44726 canvas.on('touchend', this._handleTouchEnd, this);
44729 Roo.EventManager.onWindowResize(this.resize, this, true);
44731 // file input event
44732 this.fileEl().on('change', this.uploadImage, this);
44739 resize: function(){
44741 var canvas = this.canvasEl().dom;
44742 var ctx = this.canvasElCtx();
44743 var img_data = false;
44745 if(canvas.width > 0) {
44746 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44748 // setting canvas width will clean img data
44751 var style = window.getComputedStyle ?
44752 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44754 var padding_left = parseInt(style.paddingLeft) || 0;
44755 var padding_right = parseInt(style.paddingRight) || 0;
44757 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44760 ctx.putImageData(img_data, 0, 0);
44764 _handleMouseDown: function(e)
44766 if (e.browserEvent.which === 1) {
44767 this.mouse_btn_down = true;
44768 this.strokeBegin(e);
44772 _handleMouseMove: function (e)
44774 if (this.mouse_btn_down) {
44775 this.strokeMoveUpdate(e);
44779 _handleMouseUp: function (e)
44781 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44782 this.mouse_btn_down = false;
44787 _handleTouchStart: function (e) {
44789 e.preventDefault();
44790 if (e.browserEvent.targetTouches.length === 1) {
44791 // var touch = e.browserEvent.changedTouches[0];
44792 // this.strokeBegin(touch);
44794 this.strokeBegin(e); // assume e catching the correct xy...
44798 _handleTouchMove: function (e) {
44799 e.preventDefault();
44800 // var touch = event.targetTouches[0];
44801 // _this._strokeMoveUpdate(touch);
44802 this.strokeMoveUpdate(e);
44805 _handleTouchEnd: function (e) {
44806 var wasCanvasTouched = e.target === this.canvasEl().dom;
44807 if (wasCanvasTouched) {
44808 e.preventDefault();
44809 // var touch = event.changedTouches[0];
44810 // _this._strokeEnd(touch);
44815 reset: function () {
44816 this._lastPoints = [];
44817 this._lastVelocity = 0;
44818 this._lastWidth = (this.min_width + this.max_width) / 2;
44819 this.canvasElCtx().fillStyle = this.dot_color;
44822 strokeMoveUpdate: function(e)
44824 this.strokeUpdate(e);
44826 if (this.throttle) {
44827 this.throttleStroke(this.strokeUpdate, this.throttle);
44830 this.strokeUpdate(e);
44834 strokeBegin: function(e)
44836 var newPointGroup = {
44837 color: this.dot_color,
44841 if (typeof this.onBegin === 'function') {
44845 this.curve_data.push(newPointGroup);
44847 this.strokeUpdate(e);
44850 strokeUpdate: function(e)
44852 var rect = this.canvasEl().dom.getBoundingClientRect();
44853 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44854 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44855 var lastPoints = lastPointGroup.points;
44856 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44857 var isLastPointTooClose = lastPoint
44858 ? point.distanceTo(lastPoint) <= this.min_distance
44860 var color = lastPointGroup.color;
44861 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44862 var curve = this.addPoint(point);
44864 this.drawDot({color: color, point: point});
44867 this.drawCurve({color: color, curve: curve});
44877 strokeEnd: function(e)
44879 this.strokeUpdate(e);
44880 if (typeof this.onEnd === 'function') {
44885 addPoint: function (point) {
44886 var _lastPoints = this._lastPoints;
44887 _lastPoints.push(point);
44888 if (_lastPoints.length > 2) {
44889 if (_lastPoints.length === 3) {
44890 _lastPoints.unshift(_lastPoints[0]);
44892 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44893 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44894 _lastPoints.shift();
44900 calculateCurveWidths: function (startPoint, endPoint) {
44901 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44902 (1 - this.velocity_filter_weight) * this._lastVelocity;
44904 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44907 start: this._lastWidth
44910 this._lastVelocity = velocity;
44911 this._lastWidth = newWidth;
44915 drawDot: function (_a) {
44916 var color = _a.color, point = _a.point;
44917 var ctx = this.canvasElCtx();
44918 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44920 this.drawCurveSegment(point.x, point.y, width);
44922 ctx.fillStyle = color;
44926 drawCurve: function (_a) {
44927 var color = _a.color, curve = _a.curve;
44928 var ctx = this.canvasElCtx();
44929 var widthDelta = curve.endWidth - curve.startWidth;
44930 var drawSteps = Math.floor(curve.length()) * 2;
44932 ctx.fillStyle = color;
44933 for (var i = 0; i < drawSteps; i += 1) {
44934 var t = i / drawSteps;
44940 var x = uuu * curve.startPoint.x;
44941 x += 3 * uu * t * curve.control1.x;
44942 x += 3 * u * tt * curve.control2.x;
44943 x += ttt * curve.endPoint.x;
44944 var y = uuu * curve.startPoint.y;
44945 y += 3 * uu * t * curve.control1.y;
44946 y += 3 * u * tt * curve.control2.y;
44947 y += ttt * curve.endPoint.y;
44948 var width = curve.startWidth + ttt * widthDelta;
44949 this.drawCurveSegment(x, y, width);
44955 drawCurveSegment: function (x, y, width) {
44956 var ctx = this.canvasElCtx();
44958 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44959 this.is_empty = false;
44964 var ctx = this.canvasElCtx();
44965 var canvas = this.canvasEl().dom;
44966 ctx.fillStyle = this.bg_color;
44967 ctx.clearRect(0, 0, canvas.width, canvas.height);
44968 ctx.fillRect(0, 0, canvas.width, canvas.height);
44969 this.curve_data = [];
44971 this.is_empty = true;
44976 return this.el.select('input',true).first();
44979 canvasEl: function()
44981 return this.el.select('canvas',true).first();
44984 canvasElCtx: function()
44986 return this.el.select('canvas',true).first().dom.getContext('2d');
44989 getImage: function(type)
44991 if(this.is_empty) {
44996 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44999 drawFromImage: function(img_src)
45001 var img = new Image();
45003 img.onload = function(){
45004 this.canvasElCtx().drawImage(img, 0, 0);
45009 this.is_empty = false;
45012 selectImage: function()
45014 this.fileEl().dom.click();
45017 uploadImage: function(e)
45019 var reader = new FileReader();
45021 reader.onload = function(e){
45022 var img = new Image();
45023 img.onload = function(){
45025 this.canvasElCtx().drawImage(img, 0, 0);
45027 img.src = e.target.result;
45030 reader.readAsDataURL(e.target.files[0]);
45033 // Bezier Point Constructor
45034 Point: (function () {
45035 function Point(x, y, time) {
45038 this.time = time || Date.now();
45040 Point.prototype.distanceTo = function (start) {
45041 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45043 Point.prototype.equals = function (other) {
45044 return this.x === other.x && this.y === other.y && this.time === other.time;
45046 Point.prototype.velocityFrom = function (start) {
45047 return this.time !== start.time
45048 ? this.distanceTo(start) / (this.time - start.time)
45055 // Bezier Constructor
45056 Bezier: (function () {
45057 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45058 this.startPoint = startPoint;
45059 this.control2 = control2;
45060 this.control1 = control1;
45061 this.endPoint = endPoint;
45062 this.startWidth = startWidth;
45063 this.endWidth = endWidth;
45065 Bezier.fromPoints = function (points, widths, scope) {
45066 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45067 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45068 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45070 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45071 var dx1 = s1.x - s2.x;
45072 var dy1 = s1.y - s2.y;
45073 var dx2 = s2.x - s3.x;
45074 var dy2 = s2.y - s3.y;
45075 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45076 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45077 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45078 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45079 var dxm = m1.x - m2.x;
45080 var dym = m1.y - m2.y;
45081 var k = l2 / (l1 + l2);
45082 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45083 var tx = s2.x - cm.x;
45084 var ty = s2.y - cm.y;
45086 c1: new scope.Point(m1.x + tx, m1.y + ty),
45087 c2: new scope.Point(m2.x + tx, m2.y + ty)
45090 Bezier.prototype.length = function () {
45095 for (var i = 0; i <= steps; i += 1) {
45097 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45098 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45100 var xdiff = cx - px;
45101 var ydiff = cy - py;
45102 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45109 Bezier.prototype.point = function (t, start, c1, c2, end) {
45110 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45111 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45112 + (3.0 * c2 * (1.0 - t) * t * t)
45113 + (end * t * t * t);
45118 throttleStroke: function(fn, wait) {
45119 if (wait === void 0) { wait = 250; }
45121 var timeout = null;
45125 var later = function () {
45126 previous = Date.now();
45128 result = fn.apply(storedContext, storedArgs);
45130 storedContext = null;
45134 return function wrapper() {
45136 for (var _i = 0; _i < arguments.length; _i++) {
45137 args[_i] = arguments[_i];
45139 var now = Date.now();
45140 var remaining = wait - (now - previous);
45141 storedContext = this;
45143 if (remaining <= 0 || remaining > wait) {
45145 clearTimeout(timeout);
45149 result = fn.apply(storedContext, storedArgs);
45151 storedContext = null;
45155 else if (!timeout) {
45156 timeout = window.setTimeout(later, remaining);