roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
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.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
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
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
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)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
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
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
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";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
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";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
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>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
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())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
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];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
334         }
335         
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;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
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);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
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') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
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.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
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);
514                 }
515             }
516            
517             
518            
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);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
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)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
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])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
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
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
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]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
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);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
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
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
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.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
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)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
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';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
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') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
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;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
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));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
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; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
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
1477  *
1478  * 
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
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
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
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
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%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
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)
1975  * 
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
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
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)
1990  *
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)
1998  *
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)
2004  
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
2009  * 
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...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
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.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
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
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
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 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : 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, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
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
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
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]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
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>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
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'
2351             });
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);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
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();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
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.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
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
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = 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];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
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';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
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.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
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);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
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);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
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);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
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';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
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
2865  *
2866  * 
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.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
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
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
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)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
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
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
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
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
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.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
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);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
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);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
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.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
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);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
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);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
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. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
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'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
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
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
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
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
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
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
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
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
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');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
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;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
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)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
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'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
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)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
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)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
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?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
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.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
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
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
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();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
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)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
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)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
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');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
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','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
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)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
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.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
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);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
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,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
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"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
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">&#160;</div></div></div>'
5078                 });
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){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
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 '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
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()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
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');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
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
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
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));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
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:
5196          * <pre>
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 '&#160;')
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
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
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";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
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);
5317             }
5318             return this;
5319         },
5320
5321         /**
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
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
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
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
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
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
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
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
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
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
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)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
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?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
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');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
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..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
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?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
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)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
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 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
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';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
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;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
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  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
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;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
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;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
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();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
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));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
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.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
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');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
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);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
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)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
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 }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
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));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
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
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
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
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
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
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  /**
7311  * @extends Roo.dd.DDProxy
7312  * @class Roo.grid.SplitDragZone
7313  * Support for Column Header resizing
7314  * @constructor
7315  * @param {Object} config
7316  */
7317 // private
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7320     this.grid = grid;
7321     this.view = grid.getView();
7322     this.proxy = this.view.resizeProxy;
7323     Roo.grid.SplitDragZone.superclass.constructor.call(
7324         this,
7325         hd, // ID
7326         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7327         {  // CONFIG
7328             dragElId : Roo.id(this.proxy.dom),
7329             resizeFrame:false
7330         }
7331     );
7332     
7333     this.setHandleElId(Roo.id(hd));
7334     if (hd2 !== false) {
7335         this.setOuterHandleElId(Roo.id(hd2));
7336     }
7337     
7338     this.scroll = false;
7339 };
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341     fly: Roo.Element.fly,
7342
7343     b4StartDrag : function(x, y){
7344         this.view.headersDisabled = true;
7345         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7347         );
7348         this.proxy.setHeight(h);
7349         
7350         // for old system colWidth really stored the actual width?
7351         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352         // which in reality did not work.. - it worked only for fixed sizes
7353         // for resizable we need to use actual sizes.
7354         var w = this.cm.getColumnWidth(this.cellIndex);
7355         if (!this.view.mainWrap) {
7356             // bootstrap.
7357             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7358         }
7359         
7360         
7361         
7362         // this was w-this.grid.minColumnWidth;
7363         // doesnt really make sense? - w = thie curren width or the rendered one?
7364         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365         this.resetConstraints();
7366         this.setXConstraint(minw, 1000);
7367         this.setYConstraint(0, 0);
7368         this.minX = x - minw;
7369         this.maxX = x + 1000;
7370         this.startPos = x;
7371         if (!this.view.mainWrap) { // this is Bootstrap code..
7372             this.getDragEl().style.display='block';
7373         }
7374         
7375         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7376     },
7377
7378
7379     handleMouseDown : function(e){
7380         ev = Roo.EventObject.setEvent(e);
7381         var t = this.fly(ev.getTarget());
7382         if(t.hasClass("x-grid-split")){
7383             this.cellIndex = this.view.getCellIndex(t.dom);
7384             this.split = t.dom;
7385             this.cm = this.grid.colModel;
7386             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7388             }
7389         }
7390     },
7391
7392     endDrag : function(e){
7393         this.view.headersDisabled = false;
7394         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395         var diff = endX - this.startPos;
7396         // 
7397         var w = this.cm.getColumnWidth(this.cellIndex);
7398         if (!this.view.mainWrap) {
7399             w = 0;
7400         }
7401         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7402     },
7403
7404     autoOffset : function(){
7405         this.setDelta(0,0);
7406     }
7407 });/*
7408  * Based on:
7409  * Ext JS Library 1.1.1
7410  * Copyright(c) 2006-2007, Ext JS, LLC.
7411  *
7412  * Originally Released Under LGPL - original licence link has changed is not relivant.
7413  *
7414  * Fork - LGPL
7415  * <script type="text/javascript">
7416  */
7417
7418 /**
7419  * @class Roo.grid.AbstractSelectionModel
7420  * @extends Roo.util.Observable
7421  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7422  * implemented by descendant classes.  This class should not be directly instantiated.
7423  * @constructor
7424  */
7425 Roo.grid.AbstractSelectionModel = function(){
7426     this.locked = false;
7427     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7428 };
7429
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7431     /** @ignore Called by the grid automatically. Do not call directly. */
7432     init : function(grid){
7433         this.grid = grid;
7434         this.initEvents();
7435     },
7436
7437     /**
7438      * Locks the selections.
7439      */
7440     lock : function(){
7441         this.locked = true;
7442     },
7443
7444     /**
7445      * Unlocks the selections.
7446      */
7447     unlock : function(){
7448         this.locked = false;
7449     },
7450
7451     /**
7452      * Returns true if the selections are locked.
7453      * @return {Boolean}
7454      */
7455     isLocked : function(){
7456         return this.locked;
7457     }
7458 });/*
7459  * Based on:
7460  * Ext JS Library 1.1.1
7461  * Copyright(c) 2006-2007, Ext JS, LLC.
7462  *
7463  * Originally Released Under LGPL - original licence link has changed is not relivant.
7464  *
7465  * Fork - LGPL
7466  * <script type="text/javascript">
7467  */
7468 /**
7469  * @extends Roo.grid.AbstractSelectionModel
7470  * @class Roo.grid.RowSelectionModel
7471  * The default SelectionModel used by {@link Roo.grid.Grid}.
7472  * It supports multiple selections and keyboard selection/navigation. 
7473  * @constructor
7474  * @param {Object} config
7475  */
7476 Roo.grid.RowSelectionModel = function(config){
7477     Roo.apply(this, config);
7478     this.selections = new Roo.util.MixedCollection(false, function(o){
7479         return o.id;
7480     });
7481
7482     this.last = false;
7483     this.lastActive = false;
7484
7485     this.addEvents({
7486         /**
7487         * @event selectionchange
7488         * Fires when the selection changes
7489         * @param {SelectionModel} this
7490         */
7491        "selectionchange" : true,
7492        /**
7493         * @event afterselectionchange
7494         * Fires after the selection changes (eg. by key press or clicking)
7495         * @param {SelectionModel} this
7496         */
7497        "afterselectionchange" : true,
7498        /**
7499         * @event beforerowselect
7500         * Fires when a row is selected being selected, return false to cancel.
7501         * @param {SelectionModel} this
7502         * @param {Number} rowIndex The selected index
7503         * @param {Boolean} keepExisting False if other selections will be cleared
7504         */
7505        "beforerowselect" : true,
7506        /**
7507         * @event rowselect
7508         * Fires when a row is selected.
7509         * @param {SelectionModel} this
7510         * @param {Number} rowIndex The selected index
7511         * @param {Roo.data.Record} r The record
7512         */
7513        "rowselect" : true,
7514        /**
7515         * @event rowdeselect
7516         * Fires when a row is deselected.
7517         * @param {SelectionModel} this
7518         * @param {Number} rowIndex The selected index
7519         */
7520         "rowdeselect" : true
7521     });
7522     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523     this.locked = false;
7524 };
7525
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7527     /**
7528      * @cfg {Boolean} singleSelect
7529      * True to allow selection of only one row at a time (defaults to false)
7530      */
7531     singleSelect : false,
7532
7533     // private
7534     initEvents : function(){
7535
7536         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537             this.grid.on("mousedown", this.handleMouseDown, this);
7538         }else{ // allow click to work like normal
7539             this.grid.on("rowclick", this.handleDragableRowClick, this);
7540         }
7541         // bootstrap does not have a view..
7542         var view = this.grid.view ? this.grid.view : this.grid;
7543         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7544             "up" : function(e){
7545                 if(!e.shiftKey){
7546                     this.selectPrevious(e.shiftKey);
7547                 }else if(this.last !== false && this.lastActive !== false){
7548                     var last = this.last;
7549                     this.selectRange(this.last,  this.lastActive-1);
7550                     view.focusRow(this.lastActive);
7551                     if(last !== false){
7552                         this.last = last;
7553                     }
7554                 }else{
7555                     this.selectFirstRow();
7556                 }
7557                 this.fireEvent("afterselectionchange", this);
7558             },
7559             "down" : function(e){
7560                 if(!e.shiftKey){
7561                     this.selectNext(e.shiftKey);
7562                 }else if(this.last !== false && this.lastActive !== false){
7563                     var last = this.last;
7564                     this.selectRange(this.last,  this.lastActive+1);
7565                     view.focusRow(this.lastActive);
7566                     if(last !== false){
7567                         this.last = last;
7568                     }
7569                 }else{
7570                     this.selectFirstRow();
7571                 }
7572                 this.fireEvent("afterselectionchange", this);
7573             },
7574             scope: this
7575         });
7576
7577          
7578         view.on("refresh", this.onRefresh, this);
7579         view.on("rowupdated", this.onRowUpdated, this);
7580         view.on("rowremoved", this.onRemove, this);
7581     },
7582
7583     // private
7584     onRefresh : function(){
7585         var ds = this.grid.ds, i, v = this.grid.view;
7586         var s = this.selections;
7587         s.each(function(r){
7588             if((i = ds.indexOfId(r.id)) != -1){
7589                 v.onRowSelect(i);
7590                 s.add(ds.getAt(i)); // updating the selection relate data
7591             }else{
7592                 s.remove(r);
7593             }
7594         });
7595     },
7596
7597     // private
7598     onRemove : function(v, index, r){
7599         this.selections.remove(r);
7600     },
7601
7602     // private
7603     onRowUpdated : function(v, index, r){
7604         if(this.isSelected(r)){
7605             v.onRowSelect(index);
7606         }
7607     },
7608
7609     /**
7610      * Select records.
7611      * @param {Array} records The records to select
7612      * @param {Boolean} keepExisting (optional) True to keep existing selections
7613      */
7614     selectRecords : function(records, keepExisting){
7615         if(!keepExisting){
7616             this.clearSelections();
7617         }
7618         var ds = this.grid.ds;
7619         for(var i = 0, len = records.length; i < len; i++){
7620             this.selectRow(ds.indexOf(records[i]), true);
7621         }
7622     },
7623
7624     /**
7625      * Gets the number of selected rows.
7626      * @return {Number}
7627      */
7628     getCount : function(){
7629         return this.selections.length;
7630     },
7631
7632     /**
7633      * Selects the first row in the grid.
7634      */
7635     selectFirstRow : function(){
7636         this.selectRow(0);
7637     },
7638
7639     /**
7640      * Select the last row.
7641      * @param {Boolean} keepExisting (optional) True to keep existing selections
7642      */
7643     selectLastRow : function(keepExisting){
7644         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7645     },
7646
7647     /**
7648      * Selects the row immediately following the last selected row.
7649      * @param {Boolean} keepExisting (optional) True to keep existing selections
7650      */
7651     selectNext : function(keepExisting){
7652         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653             this.selectRow(this.last+1, keepExisting);
7654             var view = this.grid.view ? this.grid.view : this.grid;
7655             view.focusRow(this.last);
7656         }
7657     },
7658
7659     /**
7660      * Selects the row that precedes the last selected row.
7661      * @param {Boolean} keepExisting (optional) True to keep existing selections
7662      */
7663     selectPrevious : function(keepExisting){
7664         if(this.last){
7665             this.selectRow(this.last-1, keepExisting);
7666             var view = this.grid.view ? this.grid.view : this.grid;
7667             view.focusRow(this.last);
7668         }
7669     },
7670
7671     /**
7672      * Returns the selected records
7673      * @return {Array} Array of selected records
7674      */
7675     getSelections : function(){
7676         return [].concat(this.selections.items);
7677     },
7678
7679     /**
7680      * Returns the first selected record.
7681      * @return {Record}
7682      */
7683     getSelected : function(){
7684         return this.selections.itemAt(0);
7685     },
7686
7687
7688     /**
7689      * Clears all selections.
7690      */
7691     clearSelections : function(fast){
7692         if(this.locked) {
7693             return;
7694         }
7695         if(fast !== true){
7696             var ds = this.grid.ds;
7697             var s = this.selections;
7698             s.each(function(r){
7699                 this.deselectRow(ds.indexOfId(r.id));
7700             }, this);
7701             s.clear();
7702         }else{
7703             this.selections.clear();
7704         }
7705         this.last = false;
7706     },
7707
7708
7709     /**
7710      * Selects all rows.
7711      */
7712     selectAll : function(){
7713         if(this.locked) {
7714             return;
7715         }
7716         this.selections.clear();
7717         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718             this.selectRow(i, true);
7719         }
7720     },
7721
7722     /**
7723      * Returns True if there is a selection.
7724      * @return {Boolean}
7725      */
7726     hasSelection : function(){
7727         return this.selections.length > 0;
7728     },
7729
7730     /**
7731      * Returns True if the specified row is selected.
7732      * @param {Number/Record} record The record or index of the record to check
7733      * @return {Boolean}
7734      */
7735     isSelected : function(index){
7736         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737         return (r && this.selections.key(r.id) ? true : false);
7738     },
7739
7740     /**
7741      * Returns True if the specified record id is selected.
7742      * @param {String} id The id of record to check
7743      * @return {Boolean}
7744      */
7745     isIdSelected : function(id){
7746         return (this.selections.key(id) ? true : false);
7747     },
7748
7749     // private
7750     handleMouseDown : function(e, t)
7751     {
7752         var view = this.grid.view ? this.grid.view : this.grid;
7753         var rowIndex;
7754         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7755             return;
7756         };
7757         if(e.shiftKey && this.last !== false){
7758             var last = this.last;
7759             this.selectRange(last, rowIndex, e.ctrlKey);
7760             this.last = last; // reset the last
7761             view.focusRow(rowIndex);
7762         }else{
7763             var isSelected = this.isSelected(rowIndex);
7764             if(e.button !== 0 && isSelected){
7765                 view.focusRow(rowIndex);
7766             }else if(e.ctrlKey && isSelected){
7767                 this.deselectRow(rowIndex);
7768             }else if(!isSelected){
7769                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770                 view.focusRow(rowIndex);
7771             }
7772         }
7773         this.fireEvent("afterselectionchange", this);
7774     },
7775     // private
7776     handleDragableRowClick :  function(grid, rowIndex, e) 
7777     {
7778         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779             this.selectRow(rowIndex, false);
7780             var view = this.grid.view ? this.grid.view : this.grid;
7781             view.focusRow(rowIndex);
7782              this.fireEvent("afterselectionchange", this);
7783         }
7784     },
7785     
7786     /**
7787      * Selects multiple rows.
7788      * @param {Array} rows Array of the indexes of the row to select
7789      * @param {Boolean} keepExisting (optional) True to keep existing selections
7790      */
7791     selectRows : function(rows, keepExisting){
7792         if(!keepExisting){
7793             this.clearSelections();
7794         }
7795         for(var i = 0, len = rows.length; i < len; i++){
7796             this.selectRow(rows[i], true);
7797         }
7798     },
7799
7800     /**
7801      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802      * @param {Number} startRow The index of the first row in the range
7803      * @param {Number} endRow The index of the last row in the range
7804      * @param {Boolean} keepExisting (optional) True to retain existing selections
7805      */
7806     selectRange : function(startRow, endRow, keepExisting){
7807         if(this.locked) {
7808             return;
7809         }
7810         if(!keepExisting){
7811             this.clearSelections();
7812         }
7813         if(startRow <= endRow){
7814             for(var i = startRow; i <= endRow; i++){
7815                 this.selectRow(i, true);
7816             }
7817         }else{
7818             for(var i = startRow; i >= endRow; i--){
7819                 this.selectRow(i, true);
7820             }
7821         }
7822     },
7823
7824     /**
7825      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826      * @param {Number} startRow The index of the first row in the range
7827      * @param {Number} endRow The index of the last row in the range
7828      */
7829     deselectRange : function(startRow, endRow, preventViewNotify){
7830         if(this.locked) {
7831             return;
7832         }
7833         for(var i = startRow; i <= endRow; i++){
7834             this.deselectRow(i, preventViewNotify);
7835         }
7836     },
7837
7838     /**
7839      * Selects a row.
7840      * @param {Number} row The index of the row to select
7841      * @param {Boolean} keepExisting (optional) True to keep existing selections
7842      */
7843     selectRow : function(index, keepExisting, preventViewNotify){
7844         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7845             return;
7846         }
7847         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848             if(!keepExisting || this.singleSelect){
7849                 this.clearSelections();
7850             }
7851             var r = this.grid.ds.getAt(index);
7852             this.selections.add(r);
7853             this.last = this.lastActive = index;
7854             if(!preventViewNotify){
7855                 var view = this.grid.view ? this.grid.view : this.grid;
7856                 view.onRowSelect(index);
7857             }
7858             this.fireEvent("rowselect", this, index, r);
7859             this.fireEvent("selectionchange", this);
7860         }
7861     },
7862
7863     /**
7864      * Deselects a row.
7865      * @param {Number} row The index of the row to deselect
7866      */
7867     deselectRow : function(index, preventViewNotify){
7868         if(this.locked) {
7869             return;
7870         }
7871         if(this.last == index){
7872             this.last = false;
7873         }
7874         if(this.lastActive == index){
7875             this.lastActive = false;
7876         }
7877         var r = this.grid.ds.getAt(index);
7878         this.selections.remove(r);
7879         if(!preventViewNotify){
7880             var view = this.grid.view ? this.grid.view : this.grid;
7881             view.onRowDeselect(index);
7882         }
7883         this.fireEvent("rowdeselect", this, index);
7884         this.fireEvent("selectionchange", this);
7885     },
7886
7887     // private
7888     restoreLast : function(){
7889         if(this._last){
7890             this.last = this._last;
7891         }
7892     },
7893
7894     // private
7895     acceptsNav : function(row, col, cm){
7896         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7897     },
7898
7899     // private
7900     onEditorKey : function(field, e){
7901         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7902         if(k == e.TAB){
7903             e.stopEvent();
7904             ed.completeEdit();
7905             if(e.shiftKey){
7906                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7907             }else{
7908                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7909             }
7910         }else if(k == e.ENTER && !e.ctrlKey){
7911             e.stopEvent();
7912             ed.completeEdit();
7913             if(e.shiftKey){
7914                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7915             }else{
7916                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7917             }
7918         }else if(k == e.ESC){
7919             ed.cancelEdit();
7920         }
7921         if(newCell){
7922             g.startEditing(newCell[0], newCell[1]);
7923         }
7924     }
7925 });/*
7926  * Based on:
7927  * Ext JS Library 1.1.1
7928  * Copyright(c) 2006-2007, Ext JS, LLC.
7929  *
7930  * Originally Released Under LGPL - original licence link has changed is not relivant.
7931  *
7932  * Fork - LGPL
7933  * <script type="text/javascript">
7934  */
7935  
7936
7937 /**
7938  * @class Roo.grid.ColumnModel
7939  * @extends Roo.util.Observable
7940  * This is the default implementation of a ColumnModel used by the Grid. It defines
7941  * the columns in the grid.
7942  * <br>Usage:<br>
7943  <pre><code>
7944  var colModel = new Roo.grid.ColumnModel([
7945         {header: "Ticker", width: 60, sortable: true, locked: true},
7946         {header: "Company Name", width: 150, sortable: true},
7947         {header: "Market Cap.", width: 100, sortable: true},
7948         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949         {header: "Employees", width: 100, sortable: true, resizable: false}
7950  ]);
7951  </code></pre>
7952  * <p>
7953  
7954  * The config options listed for this class are options which may appear in each
7955  * individual column definition.
7956  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7957  * @constructor
7958  * @param {Object} config An Array of column config objects. See this class's
7959  * config objects for details.
7960 */
7961 Roo.grid.ColumnModel = function(config){
7962         /**
7963      * The config passed into the constructor
7964      */
7965     this.config = []; //config;
7966     this.lookup = {};
7967
7968     // if no id, create one
7969     // if the column does not have a dataIndex mapping,
7970     // map it to the order it is in the config
7971     for(var i = 0, len = config.length; i < len; i++){
7972         this.addColumn(config[i]);
7973         
7974     }
7975
7976     /**
7977      * The width of columns which have no width specified (defaults to 100)
7978      * @type Number
7979      */
7980     this.defaultWidth = 100;
7981
7982     /**
7983      * Default sortable of columns which have no sortable specified (defaults to false)
7984      * @type Boolean
7985      */
7986     this.defaultSortable = false;
7987
7988     this.addEvents({
7989         /**
7990              * @event widthchange
7991              * Fires when the width of a column changes.
7992              * @param {ColumnModel} this
7993              * @param {Number} columnIndex The column index
7994              * @param {Number} newWidth The new width
7995              */
7996             "widthchange": true,
7997         /**
7998              * @event headerchange
7999              * Fires when the text of a header changes.
8000              * @param {ColumnModel} this
8001              * @param {Number} columnIndex The column index
8002              * @param {Number} newText The new header text
8003              */
8004             "headerchange": true,
8005         /**
8006              * @event hiddenchange
8007              * Fires when a column is hidden or "unhidden".
8008              * @param {ColumnModel} this
8009              * @param {Number} columnIndex The column index
8010              * @param {Boolean} hidden true if hidden, false otherwise
8011              */
8012             "hiddenchange": true,
8013             /**
8014          * @event columnmoved
8015          * Fires when a column is moved.
8016          * @param {ColumnModel} this
8017          * @param {Number} oldIndex
8018          * @param {Number} newIndex
8019          */
8020         "columnmoved" : true,
8021         /**
8022          * @event columlockchange
8023          * Fires when a column's locked state is changed
8024          * @param {ColumnModel} this
8025          * @param {Number} colIndex
8026          * @param {Boolean} locked true if locked
8027          */
8028         "columnlockchange" : true
8029     });
8030     Roo.grid.ColumnModel.superclass.constructor.call(this);
8031 };
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8033     /**
8034      * @cfg {String} header The header text to display in the Grid view.
8035      */
8036         /**
8037      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8038      */
8039         /**
8040      * @cfg {String} smHeader Header at Bootsrap Small width
8041      */
8042         /**
8043      * @cfg {String} mdHeader Header at Bootsrap Medium width
8044      */
8045         /**
8046      * @cfg {String} lgHeader Header at Bootsrap Large width
8047      */
8048         /**
8049      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8050      */
8051     /**
8052      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054      * specified, the column's index is used as an index into the Record's data Array.
8055      */
8056     /**
8057      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8059      */
8060     /**
8061      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062      * Defaults to the value of the {@link #defaultSortable} property.
8063      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8064      */
8065     /**
8066      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8067      */
8068     /**
8069      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8070      */
8071     /**
8072      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8073      */
8074     /**
8075      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8076      */
8077     /**
8078      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8082      */
8083        /**
8084      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8085      */
8086     /**
8087      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8088      */
8089     /**
8090      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8091      */
8092     /**
8093      * @cfg {String} cursor (Optional)
8094      */
8095     /**
8096      * @cfg {String} tooltip (Optional)
8097      */
8098     /**
8099      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8100      */
8101     /**
8102      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8103      */
8104     /**
8105      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8106      */
8107     /**
8108      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8109      */
8110         /**
8111      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8112      */
8113     /**
8114      * Returns the id of the column at the specified index.
8115      * @param {Number} index The column index
8116      * @return {String} the id
8117      */
8118     getColumnId : function(index){
8119         return this.config[index].id;
8120     },
8121
8122     /**
8123      * Returns the column for a specified id.
8124      * @param {String} id The column id
8125      * @return {Object} the column
8126      */
8127     getColumnById : function(id){
8128         return this.lookup[id];
8129     },
8130
8131     
8132     /**
8133      * Returns the column Object for a specified dataIndex.
8134      * @param {String} dataIndex The column dataIndex
8135      * @return {Object|Boolean} the column or false if not found
8136      */
8137     getColumnByDataIndex: function(dataIndex){
8138         var index = this.findColumnIndex(dataIndex);
8139         return index > -1 ? this.config[index] : false;
8140     },
8141     
8142     /**
8143      * Returns the index for a specified column id.
8144      * @param {String} id The column id
8145      * @return {Number} the index, or -1 if not found
8146      */
8147     getIndexById : function(id){
8148         for(var i = 0, len = this.config.length; i < len; i++){
8149             if(this.config[i].id == id){
8150                 return i;
8151             }
8152         }
8153         return -1;
8154     },
8155     
8156     /**
8157      * Returns the index for a specified column dataIndex.
8158      * @param {String} dataIndex The column dataIndex
8159      * @return {Number} the index, or -1 if not found
8160      */
8161     
8162     findColumnIndex : function(dataIndex){
8163         for(var i = 0, len = this.config.length; i < len; i++){
8164             if(this.config[i].dataIndex == dataIndex){
8165                 return i;
8166             }
8167         }
8168         return -1;
8169     },
8170     
8171     
8172     moveColumn : function(oldIndex, newIndex){
8173         var c = this.config[oldIndex];
8174         this.config.splice(oldIndex, 1);
8175         this.config.splice(newIndex, 0, c);
8176         this.dataMap = null;
8177         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8178     },
8179
8180     isLocked : function(colIndex){
8181         return this.config[colIndex].locked === true;
8182     },
8183
8184     setLocked : function(colIndex, value, suppressEvent){
8185         if(this.isLocked(colIndex) == value){
8186             return;
8187         }
8188         this.config[colIndex].locked = value;
8189         if(!suppressEvent){
8190             this.fireEvent("columnlockchange", this, colIndex, value);
8191         }
8192     },
8193
8194     getTotalLockedWidth : function(){
8195         var totalWidth = 0;
8196         for(var i = 0; i < this.config.length; i++){
8197             if(this.isLocked(i) && !this.isHidden(i)){
8198                 this.totalWidth += this.getColumnWidth(i);
8199             }
8200         }
8201         return totalWidth;
8202     },
8203
8204     getLockedCount : function(){
8205         for(var i = 0, len = this.config.length; i < len; i++){
8206             if(!this.isLocked(i)){
8207                 return i;
8208             }
8209         }
8210         
8211         return this.config.length;
8212     },
8213
8214     /**
8215      * Returns the number of columns.
8216      * @return {Number}
8217      */
8218     getColumnCount : function(visibleOnly){
8219         if(visibleOnly === true){
8220             var c = 0;
8221             for(var i = 0, len = this.config.length; i < len; i++){
8222                 if(!this.isHidden(i)){
8223                     c++;
8224                 }
8225             }
8226             return c;
8227         }
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233      * @param {Function} fn
8234      * @param {Object} scope (optional)
8235      * @return {Array} result
8236      */
8237     getColumnsBy : function(fn, scope){
8238         var r = [];
8239         for(var i = 0, len = this.config.length; i < len; i++){
8240             var c = this.config[i];
8241             if(fn.call(scope||this, c, i) === true){
8242                 r[r.length] = c;
8243             }
8244         }
8245         return r;
8246     },
8247
8248     /**
8249      * Returns true if the specified column is sortable.
8250      * @param {Number} col The column index
8251      * @return {Boolean}
8252      */
8253     isSortable : function(col){
8254         if(typeof this.config[col].sortable == "undefined"){
8255             return this.defaultSortable;
8256         }
8257         return this.config[col].sortable;
8258     },
8259
8260     /**
8261      * Returns the rendering (formatting) function defined for the column.
8262      * @param {Number} col The column index.
8263      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8264      */
8265     getRenderer : function(col){
8266         if(!this.config[col].renderer){
8267             return Roo.grid.ColumnModel.defaultRenderer;
8268         }
8269         return this.config[col].renderer;
8270     },
8271
8272     /**
8273      * Sets the rendering (formatting) function for a column.
8274      * @param {Number} col The column index
8275      * @param {Function} fn The function to use to process the cell's raw data
8276      * to return HTML markup for the grid view. The render function is called with
8277      * the following parameters:<ul>
8278      * <li>Data value.</li>
8279      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280      * <li>css A CSS style string to apply to the table cell.</li>
8281      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283      * <li>Row index</li>
8284      * <li>Column index</li>
8285      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8286      */
8287     setRenderer : function(col, fn){
8288         this.config[col].renderer = fn;
8289     },
8290
8291     /**
8292      * Returns the width for the specified column.
8293      * @param {Number} col The column index
8294      * @param (optional) {String} gridSize bootstrap width size.
8295      * @return {Number}
8296      */
8297     getColumnWidth : function(col, gridSize)
8298         {
8299                 var cfg = this.config[col];
8300                 
8301                 if (typeof(gridSize) == 'undefined') {
8302                         return cfg.width * 1 || this.defaultWidth;
8303                 }
8304                 if (gridSize === false) { // if we set it..
8305                         return cfg.width || false;
8306                 }
8307                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8308                 
8309                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8311                                 continue;
8312                         }
8313                         return cfg[ sizes[i] ];
8314                 }
8315                 return 1;
8316                 
8317     },
8318
8319     /**
8320      * Sets the width for a column.
8321      * @param {Number} col The column index
8322      * @param {Number} width The new width
8323      */
8324     setColumnWidth : function(col, width, suppressEvent){
8325         this.config[col].width = width;
8326         this.totalWidth = null;
8327         if(!suppressEvent){
8328              this.fireEvent("widthchange", this, col, width);
8329         }
8330     },
8331
8332     /**
8333      * Returns the total width of all columns.
8334      * @param {Boolean} includeHidden True to include hidden column widths
8335      * @return {Number}
8336      */
8337     getTotalWidth : function(includeHidden){
8338         if(!this.totalWidth){
8339             this.totalWidth = 0;
8340             for(var i = 0, len = this.config.length; i < len; i++){
8341                 if(includeHidden || !this.isHidden(i)){
8342                     this.totalWidth += this.getColumnWidth(i);
8343                 }
8344             }
8345         }
8346         return this.totalWidth;
8347     },
8348
8349     /**
8350      * Returns the header for the specified column.
8351      * @param {Number} col The column index
8352      * @return {String}
8353      */
8354     getColumnHeader : function(col){
8355         return this.config[col].header;
8356     },
8357
8358     /**
8359      * Sets the header for a column.
8360      * @param {Number} col The column index
8361      * @param {String} header The new header
8362      */
8363     setColumnHeader : function(col, header){
8364         this.config[col].header = header;
8365         this.fireEvent("headerchange", this, col, header);
8366     },
8367
8368     /**
8369      * Returns the tooltip for the specified column.
8370      * @param {Number} col The column index
8371      * @return {String}
8372      */
8373     getColumnTooltip : function(col){
8374             return this.config[col].tooltip;
8375     },
8376     /**
8377      * Sets the tooltip for a column.
8378      * @param {Number} col The column index
8379      * @param {String} tooltip The new tooltip
8380      */
8381     setColumnTooltip : function(col, tooltip){
8382             this.config[col].tooltip = tooltip;
8383     },
8384
8385     /**
8386      * Returns the dataIndex for the specified column.
8387      * @param {Number} col The column index
8388      * @return {Number}
8389      */
8390     getDataIndex : function(col){
8391         return this.config[col].dataIndex;
8392     },
8393
8394     /**
8395      * Sets the dataIndex for a column.
8396      * @param {Number} col The column index
8397      * @param {Number} dataIndex The new dataIndex
8398      */
8399     setDataIndex : function(col, dataIndex){
8400         this.config[col].dataIndex = dataIndex;
8401     },
8402
8403     
8404     
8405     /**
8406      * Returns true if the cell is editable.
8407      * @param {Number} colIndex The column index
8408      * @param {Number} rowIndex The row index - this is nto actually used..?
8409      * @return {Boolean}
8410      */
8411     isCellEditable : function(colIndex, rowIndex){
8412         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8413     },
8414
8415     /**
8416      * Returns the editor defined for the cell/column.
8417      * return false or null to disable editing.
8418      * @param {Number} colIndex The column index
8419      * @param {Number} rowIndex The row index
8420      * @return {Object}
8421      */
8422     getCellEditor : function(colIndex, rowIndex){
8423         return this.config[colIndex].editor;
8424     },
8425
8426     /**
8427      * Sets if a column is editable.
8428      * @param {Number} col The column index
8429      * @param {Boolean} editable True if the column is editable
8430      */
8431     setEditable : function(col, editable){
8432         this.config[col].editable = editable;
8433     },
8434
8435
8436     /**
8437      * Returns true if the column is hidden.
8438      * @param {Number} colIndex The column index
8439      * @return {Boolean}
8440      */
8441     isHidden : function(colIndex){
8442         return this.config[colIndex].hidden;
8443     },
8444
8445
8446     /**
8447      * Returns true if the column width cannot be changed
8448      */
8449     isFixed : function(colIndex){
8450         return this.config[colIndex].fixed;
8451     },
8452
8453     /**
8454      * Returns true if the column can be resized
8455      * @return {Boolean}
8456      */
8457     isResizable : function(colIndex){
8458         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8459     },
8460     /**
8461      * Sets if a column is hidden.
8462      * @param {Number} colIndex The column index
8463      * @param {Boolean} hidden True if the column is hidden
8464      */
8465     setHidden : function(colIndex, hidden){
8466         this.config[colIndex].hidden = hidden;
8467         this.totalWidth = null;
8468         this.fireEvent("hiddenchange", this, colIndex, hidden);
8469     },
8470
8471     /**
8472      * Sets the editor for a column.
8473      * @param {Number} col The column index
8474      * @param {Object} editor The editor object
8475      */
8476     setEditor : function(col, editor){
8477         this.config[col].editor = editor;
8478     },
8479     /**
8480      * Add a column (experimental...) - defaults to adding to the end..
8481      * @param {Object} config 
8482     */
8483     addColumn : function(c)
8484     {
8485     
8486         var i = this.config.length;
8487         this.config[i] = c;
8488         
8489         if(typeof c.dataIndex == "undefined"){
8490             c.dataIndex = i;
8491         }
8492         if(typeof c.renderer == "string"){
8493             c.renderer = Roo.util.Format[c.renderer];
8494         }
8495         if(typeof c.id == "undefined"){
8496             c.id = Roo.id();
8497         }
8498         if(c.editor && c.editor.xtype){
8499             c.editor  = Roo.factory(c.editor, Roo.grid);
8500         }
8501         if(c.editor && c.editor.isFormField){
8502             c.editor = new Roo.grid.GridEditor(c.editor);
8503         }
8504         this.lookup[c.id] = c;
8505     }
8506     
8507 });
8508
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8510 {
8511     if(typeof value == "object") {
8512         return value;
8513     }
8514         if(typeof value == "string" && value.length < 1){
8515             return "&#160;";
8516         }
8517     
8518         return String.format("{0}", value);
8519 };
8520
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8523 /*
8524  * Based on:
8525  * Ext JS Library 1.1.1
8526  * Copyright(c) 2006-2007, Ext JS, LLC.
8527  *
8528  * Originally Released Under LGPL - original licence link has changed is not relivant.
8529  *
8530  * Fork - LGPL
8531  * <script type="text/javascript">
8532  */
8533  
8534 /**
8535  * @class Roo.LoadMask
8536  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8537  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8539  * element's UpdateManager load indicator and will be destroyed after the initial load.
8540  * @constructor
8541  * Create a new LoadMask
8542  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543  * @param {Object} config The config object
8544  */
8545 Roo.LoadMask = function(el, config){
8546     this.el = Roo.get(el);
8547     Roo.apply(this, config);
8548     if(this.store){
8549         this.store.on('beforeload', this.onBeforeLoad, this);
8550         this.store.on('load', this.onLoad, this);
8551         this.store.on('loadexception', this.onLoadException, this);
8552         this.removeMask = false;
8553     }else{
8554         var um = this.el.getUpdateManager();
8555         um.showLoadIndicator = false; // disable the default indicator
8556         um.on('beforeupdate', this.onBeforeLoad, this);
8557         um.on('update', this.onLoad, this);
8558         um.on('failure', this.onLoad, this);
8559         this.removeMask = true;
8560     }
8561 };
8562
8563 Roo.LoadMask.prototype = {
8564     /**
8565      * @cfg {Boolean} removeMask
8566      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8568      */
8569     /**
8570      * @cfg {String} msg
8571      * The text to display in a centered loading message box (defaults to 'Loading...')
8572      */
8573     msg : 'Loading...',
8574     /**
8575      * @cfg {String} msgCls
8576      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8577      */
8578     msgCls : 'x-mask-loading',
8579
8580     /**
8581      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8582      * @type Boolean
8583      */
8584     disabled: false,
8585
8586     /**
8587      * Disables the mask to prevent it from being displayed
8588      */
8589     disable : function(){
8590        this.disabled = true;
8591     },
8592
8593     /**
8594      * Enables the mask so that it can be displayed
8595      */
8596     enable : function(){
8597         this.disabled = false;
8598     },
8599     
8600     onLoadException : function()
8601     {
8602         Roo.log(arguments);
8603         
8604         if (typeof(arguments[3]) != 'undefined') {
8605             Roo.MessageBox.alert("Error loading",arguments[3]);
8606         } 
8607         /*
8608         try {
8609             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8611             }   
8612         } catch(e) {
8613             
8614         }
8615         */
8616     
8617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8618     },
8619     // private
8620     onLoad : function()
8621     {
8622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8623     },
8624
8625     // private
8626     onBeforeLoad : function(){
8627         if(!this.disabled){
8628             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8629         }
8630     },
8631
8632     // private
8633     destroy : function(){
8634         if(this.store){
8635             this.store.un('beforeload', this.onBeforeLoad, this);
8636             this.store.un('load', this.onLoad, this);
8637             this.store.un('loadexception', this.onLoadException, this);
8638         }else{
8639             var um = this.el.getUpdateManager();
8640             um.un('beforeupdate', this.onBeforeLoad, this);
8641             um.un('update', this.onLoad, this);
8642             um.un('failure', this.onLoad, this);
8643         }
8644     }
8645 };/**
8646  * @class Roo.bootstrap.Table
8647  * @licence LGBL
8648  * @extends Roo.bootstrap.Component
8649  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8650  * Similar to Roo.grid.Grid
8651  * <pre><code>
8652  var table = Roo.factory({
8653     xtype : 'Table',
8654     xns : Roo.bootstrap,
8655     autoSizeColumns: true,
8656     
8657     
8658     store : {
8659         xtype : 'Store',
8660         xns : Roo.data,
8661         remoteSort : true,
8662         sortInfo : { direction : 'ASC', field: 'name' },
8663         proxy : {
8664            xtype : 'HttpProxy',
8665            xns : Roo.data,
8666            method : 'GET',
8667            url : 'https://example.com/some.data.url.json'
8668         },
8669         reader : {
8670            xtype : 'JsonReader',
8671            xns : Roo.data,
8672            fields : [ 'id', 'name', whatever' ],
8673            id : 'id',
8674            root : 'data'
8675         }
8676     },
8677     cm : [
8678         {
8679             xtype : 'ColumnModel',
8680             xns : Roo.grid,
8681             align : 'center',
8682             cursor : 'pointer',
8683             dataIndex : 'is_in_group',
8684             header : "Name",
8685             sortable : true,
8686             renderer : function(v, x , r) {  
8687             
8688                 return String.format("{0}", v)
8689             }
8690             width : 3
8691         } // more columns..
8692     ],
8693     selModel : {
8694         xtype : 'RowSelectionModel',
8695         xns : Roo.bootstrap.Table
8696         // you can add listeners to catch selection change here....
8697     }
8698      
8699
8700  });
8701  // set any options
8702  grid.render(Roo.get("some-div"));
8703 </code></pre>
8704
8705 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8706
8707
8708
8709  *
8710  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8713  * 
8714  * @cfg {String} cls table class
8715  *
8716  * 
8717  * @cfg {boolean} striped Should the rows be alternative striped
8718  * @cfg {boolean} bordered Add borders to the table
8719  * @cfg {boolean} hover Add hover highlighting
8720  * @cfg {boolean} condensed Format condensed
8721  * @cfg {boolean} responsive Format condensed
8722  * @cfg {Boolean} loadMask (true|false) default false
8723  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725  * @cfg {Boolean} rowSelection (true|false) default false
8726  * @cfg {Boolean} cellSelection (true|false) default false
8727  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8729  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8730  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8731  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8733  * 
8734  * @constructor
8735  * Create a new Table
8736  * @param {Object} config The config object
8737  */
8738
8739 Roo.bootstrap.Table = function(config)
8740 {
8741     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8742      
8743     // BC...
8744     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8748     
8749     this.view = this; // compat with grid.
8750     
8751     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8752     if (this.sm) {
8753         this.sm.grid = this;
8754         this.selModel = Roo.factory(this.sm, Roo.grid);
8755         this.sm = this.selModel;
8756         this.sm.xmodule = this.xmodule || false;
8757     }
8758     
8759     if (this.cm && typeof(this.cm.config) == 'undefined') {
8760         this.colModel = new Roo.grid.ColumnModel(this.cm);
8761         this.cm = this.colModel;
8762         this.cm.xmodule = this.xmodule || false;
8763     }
8764     if (this.store) {
8765         this.store= Roo.factory(this.store, Roo.data);
8766         this.ds = this.store;
8767         this.ds.xmodule = this.xmodule || false;
8768          
8769     }
8770     if (this.footer && this.store) {
8771         this.footer.dataSource = this.ds;
8772         this.footer = Roo.factory(this.footer);
8773     }
8774     
8775     /** @private */
8776     this.addEvents({
8777         /**
8778          * @event cellclick
8779          * Fires when a cell is clicked
8780          * @param {Roo.bootstrap.Table} this
8781          * @param {Roo.Element} el
8782          * @param {Number} rowIndex
8783          * @param {Number} columnIndex
8784          * @param {Roo.EventObject} e
8785          */
8786         "cellclick" : true,
8787         /**
8788          * @event celldblclick
8789          * Fires when a cell is double clicked
8790          * @param {Roo.bootstrap.Table} this
8791          * @param {Roo.Element} el
8792          * @param {Number} rowIndex
8793          * @param {Number} columnIndex
8794          * @param {Roo.EventObject} e
8795          */
8796         "celldblclick" : true,
8797         /**
8798          * @event rowclick
8799          * Fires when a row is clicked
8800          * @param {Roo.bootstrap.Table} this
8801          * @param {Roo.Element} el
8802          * @param {Number} rowIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "rowclick" : true,
8806         /**
8807          * @event rowdblclick
8808          * Fires when a row is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Roo.EventObject} e
8813          */
8814         "rowdblclick" : true,
8815         /**
8816          * @event mouseover
8817          * Fires when a mouseover occur
8818          * @param {Roo.bootstrap.Table} this
8819          * @param {Roo.Element} el
8820          * @param {Number} rowIndex
8821          * @param {Number} columnIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "mouseover" : true,
8825         /**
8826          * @event mouseout
8827          * Fires when a mouseout occur
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Number} columnIndex
8832          * @param {Roo.EventObject} e
8833          */
8834         "mouseout" : true,
8835         /**
8836          * @event rowclass
8837          * Fires when a row is rendered, so you can change add a style to it.
8838          * @param {Roo.bootstrap.Table} this
8839          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8840          */
8841         'rowclass' : true,
8842           /**
8843          * @event rowsrendered
8844          * Fires when all the  rows have been rendered
8845          * @param {Roo.bootstrap.Table} this
8846          */
8847         'rowsrendered' : true,
8848         /**
8849          * @event contextmenu
8850          * The raw contextmenu event for the entire grid.
8851          * @param {Roo.EventObject} e
8852          */
8853         "contextmenu" : true,
8854         /**
8855          * @event rowcontextmenu
8856          * Fires when a row is right clicked
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Number} rowIndex
8859          * @param {Roo.EventObject} e
8860          */
8861         "rowcontextmenu" : true,
8862         /**
8863          * @event cellcontextmenu
8864          * Fires when a cell is right clicked
8865          * @param {Roo.bootstrap.Table} this
8866          * @param {Number} rowIndex
8867          * @param {Number} cellIndex
8868          * @param {Roo.EventObject} e
8869          */
8870          "cellcontextmenu" : true,
8871          /**
8872          * @event headercontextmenu
8873          * Fires when a header is right clicked
8874          * @param {Roo.bootstrap.Table} this
8875          * @param {Number} columnIndex
8876          * @param {Roo.EventObject} e
8877          */
8878         "headercontextmenu" : true,
8879         /**
8880          * @event mousedown
8881          * The raw mousedown event for the entire grid.
8882          * @param {Roo.EventObject} e
8883          */
8884         "mousedown" : true
8885         
8886     });
8887 };
8888
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8890     
8891     cls: false,
8892     
8893     striped : false,
8894     scrollBody : false,
8895     bordered: false,
8896     hover:  false,
8897     condensed : false,
8898     responsive : false,
8899     sm : false,
8900     cm : false,
8901     store : false,
8902     loadMask : false,
8903     footerShow : true,
8904     headerShow : true,
8905     enableColumnResize: true,
8906   
8907     rowSelection : false,
8908     cellSelection : false,
8909     layout : false,
8910
8911     minColumnWidth : 50,
8912     
8913     // Roo.Element - the tbody
8914     bodyEl: false,  // <tbody> Roo.Element - thead element    
8915     headEl: false,  // <thead> Roo.Element - thead element
8916     resizeProxy : false, // proxy element for dragging?
8917
8918
8919     
8920     container: false, // used by gridpanel...
8921     
8922     lazyLoad : false,
8923     
8924     CSS : Roo.util.CSS,
8925     
8926     auto_hide_footer : false,
8927     
8928     view: false, // actually points to this..
8929     
8930     getAutoCreate : function()
8931     {
8932         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8933         
8934         cfg = {
8935             tag: 'table',
8936             cls : 'table', 
8937             cn : []
8938         };
8939         // this get's auto added by panel.Grid
8940         if (this.scrollBody) {
8941             cfg.cls += ' table-body-fixed';
8942         }    
8943         if (this.striped) {
8944             cfg.cls += ' table-striped';
8945         }
8946         
8947         if (this.hover) {
8948             cfg.cls += ' table-hover';
8949         }
8950         if (this.bordered) {
8951             cfg.cls += ' table-bordered';
8952         }
8953         if (this.condensed) {
8954             cfg.cls += ' table-condensed';
8955         }
8956         
8957         if (this.responsive) {
8958             cfg.cls += ' table-responsive';
8959         }
8960         
8961         if (this.cls) {
8962             cfg.cls+=  ' ' +this.cls;
8963         }
8964         
8965         
8966         
8967         if (this.layout) {
8968             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8969         }
8970         
8971         if(this.store || this.cm){
8972             if(this.headerShow){
8973                 cfg.cn.push(this.renderHeader());
8974             }
8975             
8976             cfg.cn.push(this.renderBody());
8977             
8978             if(this.footerShow){
8979                 cfg.cn.push(this.renderFooter());
8980             }
8981             // where does this come from?
8982             //cfg.cls+=  ' TableGrid';
8983         }
8984         
8985         return { cn : [ cfg ] };
8986     },
8987     
8988     initEvents : function()
8989     {   
8990         if(!this.store || !this.cm){
8991             return;
8992         }
8993         if (this.selModel) {
8994             this.selModel.initEvents();
8995         }
8996         
8997         
8998         //Roo.log('initEvents with ds!!!!');
8999         
9000         this.bodyEl = this.el.select('tbody', true).first();
9001         this.headEl = this.el.select('thead', true).first();
9002         this.mainFoot = this.el.select('tfoot', true).first();
9003         
9004         
9005         
9006         
9007         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008             e.on('click', this.sort, this);
9009         }, this);
9010         
9011         
9012         // why is this done????? = it breaks dialogs??
9013         //this.parent().el.setStyle('position', 'relative');
9014         
9015         
9016         if (this.footer) {
9017             this.footer.parentId = this.id;
9018             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9019             
9020             if(this.lazyLoad){
9021                 this.el.select('tfoot tr td').first().addClass('hide');
9022             }
9023         } 
9024         
9025         if(this.loadMask) {
9026             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9027         }
9028         
9029         this.store.on('load', this.onLoad, this);
9030         this.store.on('beforeload', this.onBeforeLoad, this);
9031         this.store.on('update', this.onUpdate, this);
9032         this.store.on('add', this.onAdd, this);
9033         this.store.on("clear", this.clear, this);
9034         
9035         this.el.on("contextmenu", this.onContextMenu, this);
9036         
9037         
9038         this.cm.on("headerchange", this.onHeaderChange, this);
9039         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9040
9041  //?? does bodyEl get replaced on render?
9042         this.bodyEl.on("click", this.onClick, this);
9043         this.bodyEl.on("dblclick", this.onDblClick, this);        
9044         this.bodyEl.on('scroll', this.onBodyScroll, this);
9045
9046         // guessing mainbody will work - this relays usually caught by selmodel at present.
9047         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9048   
9049   
9050         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9051         
9052   
9053         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9055         }
9056         
9057         this.initCSS();
9058     },
9059     // Compatibility with grid - we implement all the view features at present.
9060     getView : function()
9061     {
9062         return this;
9063     },
9064     
9065     initCSS : function()
9066     {
9067         
9068         
9069         var cm = this.cm, styles = [];
9070         this.CSS.removeStyleSheet(this.id + '-cssrules');
9071         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072         // we can honour xs/sm/md/xl  as widths...
9073         // we first have to decide what widht we are currently at...
9074         var sz = Roo.getGridSize();
9075         
9076         var total = 0;
9077         var last = -1;
9078         var cols = []; // visable cols.
9079         var total_abs = 0;
9080         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081             var w = cm.getColumnWidth(i, false);
9082             if(cm.isHidden(i)){
9083                 cols.push( { rel : false, abs : 0 });
9084                 continue;
9085             }
9086             if (w !== false) {
9087                 cols.push( { rel : false, abs : w });
9088                 total_abs += w;
9089                 last = i; // not really..
9090                 continue;
9091             }
9092             var w = cm.getColumnWidth(i, sz);
9093             if (w > 0) {
9094                 last = i
9095             }
9096             total += w;
9097             cols.push( { rel : w, abs : false });
9098         }
9099         
9100         var avail = this.bodyEl.dom.clientWidth - total_abs;
9101         
9102         var unitWidth = Math.floor(avail / total);
9103         var rem = avail - (unitWidth * total);
9104         
9105         var hidden, width, pos = 0 , splithide , left;
9106         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9107             
9108             hidden = 'display:none;';
9109             left = '';
9110             width  = 'width:0px;';
9111             splithide = '';
9112             if(!cm.isHidden(i)){
9113                 hidden = '';
9114                 
9115                 
9116                 // we can honour xs/sm/md/xl ?
9117                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9118                 if (w===0) {
9119                     hidden = 'display:none;';
9120                 }
9121                 // width should return a small number...
9122                 if (i == last) {
9123                     w+=rem; // add the remaining with..
9124                 }
9125                 pos += w;
9126                 left = "left:" + (pos -4) + "px;";
9127                 width = "width:" + w+ "px;";
9128                 
9129             }
9130             if (this.responsive) {
9131                 width = '';
9132                 left = '';
9133                 hidden = cm.isHidden(i) ? 'display:none' : '';
9134                 splithide = 'display: none';
9135             }
9136             
9137             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9138             if (this.headEl) {
9139                 if (i == last) {
9140                     splithide = 'display:none;';
9141                 }
9142                 
9143                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9144                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9145                 );
9146             }
9147             
9148         }
9149         //Roo.log(styles.join(''));
9150         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9151         
9152     },
9153     
9154     
9155     
9156     onContextMenu : function(e, t)
9157     {
9158         this.processEvent("contextmenu", e);
9159     },
9160     
9161     processEvent : function(name, e)
9162     {
9163         if (name != 'touchstart' ) {
9164             this.fireEvent(name, e);    
9165         }
9166         
9167         var t = e.getTarget();
9168         
9169         var cell = Roo.get(t);
9170         
9171         if(!cell){
9172             return;
9173         }
9174         
9175         if(cell.findParent('tfoot', false, true)){
9176             return;
9177         }
9178         
9179         if(cell.findParent('thead', false, true)){
9180             
9181             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9182                 cell = Roo.get(t).findParent('th', false, true);
9183                 if (!cell) {
9184                     Roo.log("failed to find th in thead?");
9185                     Roo.log(e.getTarget());
9186                     return;
9187                 }
9188             }
9189             
9190             var cellIndex = cell.dom.cellIndex;
9191             
9192             var ename = name == 'touchstart' ? 'click' : name;
9193             this.fireEvent("header" + ename, this, cellIndex, e);
9194             
9195             return;
9196         }
9197         
9198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9199             cell = Roo.get(t).findParent('td', false, true);
9200             if (!cell) {
9201                 Roo.log("failed to find th in tbody?");
9202                 Roo.log(e.getTarget());
9203                 return;
9204             }
9205         }
9206         
9207         var row = cell.findParent('tr', false, true);
9208         var cellIndex = cell.dom.cellIndex;
9209         var rowIndex = row.dom.rowIndex - 1;
9210         
9211         if(row !== false){
9212             
9213             this.fireEvent("row" + name, this, rowIndex, e);
9214             
9215             if(cell !== false){
9216             
9217                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9218             }
9219         }
9220         
9221     },
9222     
9223     onMouseover : function(e, el)
9224     {
9225         var cell = Roo.get(el);
9226         
9227         if(!cell){
9228             return;
9229         }
9230         
9231         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9232             cell = cell.findParent('td', false, true);
9233         }
9234         
9235         var row = cell.findParent('tr', false, true);
9236         var cellIndex = cell.dom.cellIndex;
9237         var rowIndex = row.dom.rowIndex - 1; // start from 0
9238         
9239         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9240         
9241     },
9242     
9243     onMouseout : function(e, el)
9244     {
9245         var cell = Roo.get(el);
9246         
9247         if(!cell){
9248             return;
9249         }
9250         
9251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9252             cell = cell.findParent('td', false, true);
9253         }
9254         
9255         var row = cell.findParent('tr', false, true);
9256         var cellIndex = cell.dom.cellIndex;
9257         var rowIndex = row.dom.rowIndex - 1; // start from 0
9258         
9259         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9260         
9261     },
9262     
9263     onClick : function(e, el)
9264     {
9265         var cell = Roo.get(el);
9266         
9267         if(!cell || (!this.cellSelection && !this.rowSelection)){
9268             return;
9269         }
9270         
9271         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9272             cell = cell.findParent('td', false, true);
9273         }
9274         
9275         if(!cell || typeof(cell) == 'undefined'){
9276             return;
9277         }
9278         
9279         var row = cell.findParent('tr', false, true);
9280         
9281         if(!row || typeof(row) == 'undefined'){
9282             return;
9283         }
9284         
9285         var cellIndex = cell.dom.cellIndex;
9286         var rowIndex = this.getRowIndex(row);
9287         
9288         // why??? - should these not be based on SelectionModel?
9289         //if(this.cellSelection){
9290             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9291         //}
9292         
9293         //if(this.rowSelection){
9294             this.fireEvent('rowclick', this, row, rowIndex, e);
9295         //}
9296          
9297     },
9298         
9299     onDblClick : function(e,el)
9300     {
9301         var cell = Roo.get(el);
9302         
9303         if(!cell || (!this.cellSelection && !this.rowSelection)){
9304             return;
9305         }
9306         
9307         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9308             cell = cell.findParent('td', false, true);
9309         }
9310         
9311         if(!cell || typeof(cell) == 'undefined'){
9312             return;
9313         }
9314         
9315         var row = cell.findParent('tr', false, true);
9316         
9317         if(!row || typeof(row) == 'undefined'){
9318             return;
9319         }
9320         
9321         var cellIndex = cell.dom.cellIndex;
9322         var rowIndex = this.getRowIndex(row);
9323         
9324         if(this.cellSelection){
9325             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9326         }
9327         
9328         if(this.rowSelection){
9329             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9330         }
9331     },
9332     findRowIndex : function(el)
9333     {
9334         var cell = Roo.get(el);
9335         if(!cell) {
9336             return false;
9337         }
9338         var row = cell.findParent('tr', false, true);
9339         
9340         if(!row || typeof(row) == 'undefined'){
9341             return false;
9342         }
9343         return this.getRowIndex(row);
9344     },
9345     sort : function(e,el)
9346     {
9347         var col = Roo.get(el);
9348         
9349         if(!col.hasClass('sortable')){
9350             return;
9351         }
9352         
9353         var sort = col.attr('sort');
9354         var dir = 'ASC';
9355         
9356         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9357             dir = 'DESC';
9358         }
9359         
9360         this.store.sortInfo = {field : sort, direction : dir};
9361         
9362         if (this.footer) {
9363             Roo.log("calling footer first");
9364             this.footer.onClick('first');
9365         } else {
9366         
9367             this.store.load({ params : { start : 0 } });
9368         }
9369     },
9370     
9371     renderHeader : function()
9372     {
9373         var header = {
9374             tag: 'thead',
9375             cn : []
9376         };
9377         
9378         var cm = this.cm;
9379         this.totalWidth = 0;
9380         
9381         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9382             
9383             var config = cm.config[i];
9384             
9385             var c = {
9386                 tag: 'th',
9387                 cls : 'x-hcol-' + i,
9388                 style : '',
9389                 
9390                 html: cm.getColumnHeader(i)
9391             };
9392             
9393             var tooltip = cm.getColumnTooltip(i);
9394             if (tooltip) {
9395                 c.tooltip = tooltip;
9396             }
9397             
9398             
9399             var hh = '';
9400             
9401             if(typeof(config.sortable) != 'undefined' && config.sortable){
9402                 c.cls += ' sortable';
9403                 c.html = '<i class="fa"></i>' + c.html;
9404             }
9405             
9406             // could use BS4 hidden-..-down 
9407             
9408             if(typeof(config.lgHeader) != 'undefined'){
9409                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9410             }
9411             
9412             if(typeof(config.mdHeader) != 'undefined'){
9413                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9414             }
9415             
9416             if(typeof(config.smHeader) != 'undefined'){
9417                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9418             }
9419             
9420             if(typeof(config.xsHeader) != 'undefined'){
9421                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9422             }
9423             
9424             if(hh.length){
9425                 c.html = hh;
9426             }
9427             
9428             if(typeof(config.tooltip) != 'undefined'){
9429                 c.tooltip = config.tooltip;
9430             }
9431             
9432             if(typeof(config.colspan) != 'undefined'){
9433                 c.colspan = config.colspan;
9434             }
9435             
9436             // hidden is handled by CSS now
9437             
9438             if(typeof(config.dataIndex) != 'undefined'){
9439                 c.sort = config.dataIndex;
9440             }
9441             
9442            
9443             
9444             if(typeof(config.align) != 'undefined' && config.align.length){
9445                 c.style += ' text-align:' + config.align + ';';
9446             }
9447             
9448             /* width is done in CSS
9449              *if(typeof(config.width) != 'undefined'){
9450                 c.style += ' width:' + config.width + 'px;';
9451                 this.totalWidth += config.width;
9452             } else {
9453                 this.totalWidth += 100; // assume minimum of 100 per column?
9454             }
9455             */
9456             
9457             if(typeof(config.cls) != 'undefined'){
9458                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9459             }
9460             // this is the bit that doesnt reall work at all...
9461             
9462             if (this.responsive) {
9463                  
9464             
9465                 ['xs','sm','md','lg'].map(function(size){
9466                     
9467                     if(typeof(config[size]) == 'undefined'){
9468                         return;
9469                     }
9470                      
9471                     if (!config[size]) { // 0 = hidden
9472                         // BS 4 '0' is treated as hide that column and below.
9473                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9474                         return;
9475                     }
9476                     
9477                     c.cls += ' col-' + size + '-' + config[size] + (
9478                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9479                     );
9480                     
9481                     
9482                 });
9483             }
9484             // at the end?
9485             
9486             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9487             
9488             
9489             
9490             
9491             header.cn.push(c)
9492         }
9493         
9494         return header;
9495     },
9496     
9497     renderBody : function()
9498     {
9499         var body = {
9500             tag: 'tbody',
9501             cn : [
9502                 {
9503                     tag: 'tr',
9504                     cn : [
9505                         {
9506                             tag : 'td',
9507                             colspan :  this.cm.getColumnCount()
9508                         }
9509                     ]
9510                 }
9511             ]
9512         };
9513         
9514         return body;
9515     },
9516     
9517     renderFooter : function()
9518     {
9519         var footer = {
9520             tag: 'tfoot',
9521             cn : [
9522                 {
9523                     tag: 'tr',
9524                     cn : [
9525                         {
9526                             tag : 'td',
9527                             colspan :  this.cm.getColumnCount()
9528                         }
9529                     ]
9530                 }
9531             ]
9532         };
9533         
9534         return footer;
9535     },
9536     
9537     
9538     
9539     onLoad : function()
9540     {
9541 //        Roo.log('ds onload');
9542         this.clear();
9543         
9544         var _this = this;
9545         var cm = this.cm;
9546         var ds = this.store;
9547         
9548         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9549             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9550             if (_this.store.sortInfo) {
9551                     
9552                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9553                     e.select('i', true).addClass(['fa-arrow-up']);
9554                 }
9555                 
9556                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9557                     e.select('i', true).addClass(['fa-arrow-down']);
9558                 }
9559             }
9560         });
9561         
9562         var tbody =  this.bodyEl;
9563               
9564         if(ds.getCount() > 0){
9565             ds.data.each(function(d,rowIndex){
9566                 var row =  this.renderRow(cm, ds, rowIndex);
9567                 
9568                 tbody.createChild(row);
9569                 
9570                 var _this = this;
9571                 
9572                 if(row.cellObjects.length){
9573                     Roo.each(row.cellObjects, function(r){
9574                         _this.renderCellObject(r);
9575                     })
9576                 }
9577                 
9578             }, this);
9579         }
9580         
9581         var tfoot = this.el.select('tfoot', true).first();
9582         
9583         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9584             
9585             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9586             
9587             var total = this.ds.getTotalCount();
9588             
9589             if(this.footer.pageSize < total){
9590                 this.mainFoot.show();
9591             }
9592         }
9593         
9594         Roo.each(this.el.select('tbody td', true).elements, function(e){
9595             e.on('mouseover', _this.onMouseover, _this);
9596         });
9597         
9598         Roo.each(this.el.select('tbody td', true).elements, function(e){
9599             e.on('mouseout', _this.onMouseout, _this);
9600         });
9601         this.fireEvent('rowsrendered', this);
9602         
9603         this.autoSize();
9604         
9605         this.initCSS(); /// resize cols
9606
9607         
9608     },
9609     
9610     
9611     onUpdate : function(ds,record)
9612     {
9613         this.refreshRow(record);
9614         this.autoSize();
9615     },
9616     
9617     onRemove : function(ds, record, index, isUpdate){
9618         if(isUpdate !== true){
9619             this.fireEvent("beforerowremoved", this, index, record);
9620         }
9621         var bt = this.bodyEl.dom;
9622         
9623         var rows = this.el.select('tbody > tr', true).elements;
9624         
9625         if(typeof(rows[index]) != 'undefined'){
9626             bt.removeChild(rows[index].dom);
9627         }
9628         
9629 //        if(bt.rows[index]){
9630 //            bt.removeChild(bt.rows[index]);
9631 //        }
9632         
9633         if(isUpdate !== true){
9634             //this.stripeRows(index);
9635             //this.syncRowHeights(index, index);
9636             //this.layout();
9637             this.fireEvent("rowremoved", this, index, record);
9638         }
9639     },
9640     
9641     onAdd : function(ds, records, rowIndex)
9642     {
9643         //Roo.log('on Add called');
9644         // - note this does not handle multiple adding very well..
9645         var bt = this.bodyEl.dom;
9646         for (var i =0 ; i < records.length;i++) {
9647             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9648             //Roo.log(records[i]);
9649             //Roo.log(this.store.getAt(rowIndex+i));
9650             this.insertRow(this.store, rowIndex + i, false);
9651             return;
9652         }
9653         
9654     },
9655     
9656     
9657     refreshRow : function(record){
9658         var ds = this.store, index;
9659         if(typeof record == 'number'){
9660             index = record;
9661             record = ds.getAt(index);
9662         }else{
9663             index = ds.indexOf(record);
9664             if (index < 0) {
9665                 return; // should not happen - but seems to 
9666             }
9667         }
9668         this.insertRow(ds, index, true);
9669         this.autoSize();
9670         this.onRemove(ds, record, index+1, true);
9671         this.autoSize();
9672         //this.syncRowHeights(index, index);
9673         //this.layout();
9674         this.fireEvent("rowupdated", this, index, record);
9675     },
9676     // private - called by RowSelection
9677     onRowSelect : function(rowIndex){
9678         var row = this.getRowDom(rowIndex);
9679         row.addClass(['bg-info','info']);
9680     },
9681     // private - called by RowSelection
9682     onRowDeselect : function(rowIndex)
9683     {
9684         if (rowIndex < 0) {
9685             return;
9686         }
9687         var row = this.getRowDom(rowIndex);
9688         row.removeClass(['bg-info','info']);
9689     },
9690       /**
9691      * Focuses the specified row.
9692      * @param {Number} row The row index
9693      */
9694     focusRow : function(row)
9695     {
9696         //Roo.log('GridView.focusRow');
9697         var x = this.bodyEl.dom.scrollLeft;
9698         this.focusCell(row, 0, false);
9699         this.bodyEl.dom.scrollLeft = x;
9700
9701     },
9702      /**
9703      * Focuses the specified cell.
9704      * @param {Number} row The row index
9705      * @param {Number} col The column index
9706      * @param {Boolean} hscroll false to disable horizontal scrolling
9707      */
9708     focusCell : function(row, col, hscroll)
9709     {
9710         //Roo.log('GridView.focusCell');
9711         var el = this.ensureVisible(row, col, hscroll);
9712         // not sure what focusEL achives = it's a <a> pos relative 
9713         //this.focusEl.alignTo(el, "tl-tl");
9714         //if(Roo.isGecko){
9715         //    this.focusEl.focus();
9716         //}else{
9717         //    this.focusEl.focus.defer(1, this.focusEl);
9718         //}
9719     },
9720     
9721      /**
9722      * Scrolls the specified cell into view
9723      * @param {Number} row The row index
9724      * @param {Number} col The column index
9725      * @param {Boolean} hscroll false to disable horizontal scrolling
9726      */
9727     ensureVisible : function(row, col, hscroll)
9728     {
9729         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9730         //return null; //disable for testing.
9731         if(typeof row != "number"){
9732             row = row.rowIndex;
9733         }
9734         if(row < 0 && row >= this.ds.getCount()){
9735             return  null;
9736         }
9737         col = (col !== undefined ? col : 0);
9738         var cm = this.cm;
9739         while(cm.isHidden(col)){
9740             col++;
9741         }
9742
9743         var el = this.getCellDom(row, col);
9744         if(!el){
9745             return null;
9746         }
9747         var c = this.bodyEl.dom;
9748
9749         var ctop = parseInt(el.offsetTop, 10);
9750         var cleft = parseInt(el.offsetLeft, 10);
9751         var cbot = ctop + el.offsetHeight;
9752         var cright = cleft + el.offsetWidth;
9753
9754         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9755         var ch = 0; //?? header is not withing the area?
9756         var stop = parseInt(c.scrollTop, 10);
9757         var sleft = parseInt(c.scrollLeft, 10);
9758         var sbot = stop + ch;
9759         var sright = sleft + c.clientWidth;
9760         /*
9761         Roo.log('GridView.ensureVisible:' +
9762                 ' ctop:' + ctop +
9763                 ' c.clientHeight:' + c.clientHeight +
9764                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9765                 ' stop:' + stop +
9766                 ' cbot:' + cbot +
9767                 ' sbot:' + sbot +
9768                 ' ch:' + ch  
9769                 );
9770         */
9771         if(ctop < stop){
9772             c.scrollTop = ctop;
9773             //Roo.log("set scrolltop to ctop DISABLE?");
9774         }else if(cbot > sbot){
9775             //Roo.log("set scrolltop to cbot-ch");
9776             c.scrollTop = cbot-ch;
9777         }
9778
9779         if(hscroll !== false){
9780             if(cleft < sleft){
9781                 c.scrollLeft = cleft;
9782             }else if(cright > sright){
9783                 c.scrollLeft = cright-c.clientWidth;
9784             }
9785         }
9786
9787         return el;
9788     },
9789     
9790     
9791     insertRow : function(dm, rowIndex, isUpdate){
9792         
9793         if(!isUpdate){
9794             this.fireEvent("beforerowsinserted", this, rowIndex);
9795         }
9796             //var s = this.getScrollState();
9797         var row = this.renderRow(this.cm, this.store, rowIndex);
9798         // insert before rowIndex..
9799         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9800         
9801         var _this = this;
9802                 
9803         if(row.cellObjects.length){
9804             Roo.each(row.cellObjects, function(r){
9805                 _this.renderCellObject(r);
9806             })
9807         }
9808             
9809         if(!isUpdate){
9810             this.fireEvent("rowsinserted", this, rowIndex);
9811             //this.syncRowHeights(firstRow, lastRow);
9812             //this.stripeRows(firstRow);
9813             //this.layout();
9814         }
9815         
9816     },
9817     
9818     
9819     getRowDom : function(rowIndex)
9820     {
9821         var rows = this.el.select('tbody > tr', true).elements;
9822         
9823         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9824         
9825     },
9826     getCellDom : function(rowIndex, colIndex)
9827     {
9828         var row = this.getRowDom(rowIndex);
9829         if (row === false) {
9830             return false;
9831         }
9832         var cols = row.select('td', true).elements;
9833         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9834         
9835     },
9836     
9837     // returns the object tree for a tr..
9838   
9839     
9840     renderRow : function(cm, ds, rowIndex) 
9841     {
9842         var d = ds.getAt(rowIndex);
9843         
9844         var row = {
9845             tag : 'tr',
9846             cls : 'x-row-' + rowIndex,
9847             cn : []
9848         };
9849             
9850         var cellObjects = [];
9851         
9852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9853             var config = cm.config[i];
9854             
9855             var renderer = cm.getRenderer(i);
9856             var value = '';
9857             var id = false;
9858             
9859             if(typeof(renderer) !== 'undefined'){
9860                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9861             }
9862             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9863             // and are rendered into the cells after the row is rendered - using the id for the element.
9864             
9865             if(typeof(value) === 'object'){
9866                 id = Roo.id();
9867                 cellObjects.push({
9868                     container : id,
9869                     cfg : value 
9870                 })
9871             }
9872             
9873             var rowcfg = {
9874                 record: d,
9875                 rowIndex : rowIndex,
9876                 colIndex : i,
9877                 rowClass : ''
9878             };
9879
9880             this.fireEvent('rowclass', this, rowcfg);
9881             
9882             var td = {
9883                 tag: 'td',
9884                 // this might end up displaying HTML?
9885                 // this is too messy... - better to only do it on columsn you know are going to be too long
9886                 //tooltip : (typeof(value) === 'object') ? '' : value,
9887                 cls : rowcfg.rowClass + ' x-col-' + i,
9888                 style: '',
9889                 html: (typeof(value) === 'object') ? '' : value
9890             };
9891             
9892             if (id) {
9893                 td.id = id;
9894             }
9895             
9896             if(typeof(config.colspan) != 'undefined'){
9897                 td.colspan = config.colspan;
9898             }
9899             
9900             
9901             
9902             if(typeof(config.align) != 'undefined' && config.align.length){
9903                 td.style += ' text-align:' + config.align + ';';
9904             }
9905             if(typeof(config.valign) != 'undefined' && config.valign.length){
9906                 td.style += ' vertical-align:' + config.valign + ';';
9907             }
9908             /*
9909             if(typeof(config.width) != 'undefined'){
9910                 td.style += ' width:' +  config.width + 'px;';
9911             }
9912             */
9913             
9914             if(typeof(config.cursor) != 'undefined'){
9915                 td.style += ' cursor:' +  config.cursor + ';';
9916             }
9917             
9918             if(typeof(config.cls) != 'undefined'){
9919                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9920             }
9921             if (this.responsive) {
9922                 ['xs','sm','md','lg'].map(function(size){
9923                     
9924                     if(typeof(config[size]) == 'undefined'){
9925                         return;
9926                     }
9927                     
9928                     
9929                       
9930                     if (!config[size]) { // 0 = hidden
9931                         // BS 4 '0' is treated as hide that column and below.
9932                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9933                         return;
9934                     }
9935                     
9936                     td.cls += ' col-' + size + '-' + config[size] + (
9937                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9938                     );
9939                      
9940     
9941                 });
9942             }
9943             row.cn.push(td);
9944            
9945         }
9946         
9947         row.cellObjects = cellObjects;
9948         
9949         return row;
9950           
9951     },
9952     
9953     
9954     
9955     onBeforeLoad : function()
9956     {
9957         
9958     },
9959      /**
9960      * Remove all rows
9961      */
9962     clear : function()
9963     {
9964         this.el.select('tbody', true).first().dom.innerHTML = '';
9965     },
9966     /**
9967      * Show or hide a row.
9968      * @param {Number} rowIndex to show or hide
9969      * @param {Boolean} state hide
9970      */
9971     setRowVisibility : function(rowIndex, state)
9972     {
9973         var bt = this.bodyEl.dom;
9974         
9975         var rows = this.el.select('tbody > tr', true).elements;
9976         
9977         if(typeof(rows[rowIndex]) == 'undefined'){
9978             return;
9979         }
9980         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9981         
9982     },
9983     
9984     
9985     getSelectionModel : function(){
9986         if(!this.selModel){
9987             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9988         }
9989         return this.selModel;
9990     },
9991     /*
9992      * Render the Roo.bootstrap object from renderder
9993      */
9994     renderCellObject : function(r)
9995     {
9996         var _this = this;
9997         
9998         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9999         
10000         var t = r.cfg.render(r.container);
10001         
10002         if(r.cfg.cn){
10003             Roo.each(r.cfg.cn, function(c){
10004                 var child = {
10005                     container: t.getChildContainer(),
10006                     cfg: c
10007                 };
10008                 _this.renderCellObject(child);
10009             })
10010         }
10011     },
10012     /**
10013      * get the Row Index from a dom element.
10014      * @param {Roo.Element} row The row to look for
10015      * @returns {Number} the row
10016      */
10017     getRowIndex : function(row)
10018     {
10019         var rowIndex = -1;
10020         
10021         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10022             if(el != row){
10023                 return;
10024             }
10025             
10026             rowIndex = index;
10027         });
10028         
10029         return rowIndex;
10030     },
10031     /**
10032      * get the header TH element for columnIndex
10033      * @param {Number} columnIndex
10034      * @returns {Roo.Element}
10035      */
10036     getHeaderIndex: function(colIndex)
10037     {
10038         var cols = this.headEl.select('th', true).elements;
10039         return cols[colIndex]; 
10040     },
10041     /**
10042      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10043      * @param {domElement} cell to look for
10044      * @returns {Number} the column
10045      */
10046     getCellIndex : function(cell)
10047     {
10048         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10049         if(id){
10050             return parseInt(id[1], 10);
10051         }
10052         return 0;
10053     },
10054      /**
10055      * Returns the grid's underlying element = used by panel.Grid
10056      * @return {Element} The element
10057      */
10058     getGridEl : function(){
10059         return this.el;
10060     },
10061      /**
10062      * Forces a resize - used by panel.Grid
10063      * @return {Element} The element
10064      */
10065     autoSize : function()
10066     {
10067         //var ctr = Roo.get(this.container.dom.parentElement);
10068         var ctr = Roo.get(this.el.dom);
10069         
10070         var thd = this.getGridEl().select('thead',true).first();
10071         var tbd = this.getGridEl().select('tbody', true).first();
10072         var tfd = this.getGridEl().select('tfoot', true).first();
10073         
10074         var cw = ctr.getWidth();
10075         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10076         
10077         if (tbd) {
10078             
10079             tbd.setWidth(ctr.getWidth());
10080             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10081             // this needs fixing for various usage - currently only hydra job advers I think..
10082             //tdb.setHeight(
10083             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10084             //); 
10085             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10086             cw -= barsize;
10087         }
10088         cw = Math.max(cw, this.totalWidth);
10089         this.getGridEl().select('tbody tr',true).setWidth(cw);
10090         this.initCSS();
10091         
10092         // resize 'expandable coloumn?
10093         
10094         return; // we doe not have a view in this design..
10095         
10096     },
10097     onBodyScroll: function()
10098     {
10099         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10100         if(this.headEl){
10101             this.headEl.setStyle({
10102                 'position' : 'relative',
10103                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10104             });
10105         }
10106         
10107         if(this.lazyLoad){
10108             
10109             var scrollHeight = this.bodyEl.dom.scrollHeight;
10110             
10111             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10112             
10113             var height = this.bodyEl.getHeight();
10114             
10115             if(scrollHeight - height == scrollTop) {
10116                 
10117                 var total = this.ds.getTotalCount();
10118                 
10119                 if(this.footer.cursor + this.footer.pageSize < total){
10120                     
10121                     this.footer.ds.load({
10122                         params : {
10123                             start : this.footer.cursor + this.footer.pageSize,
10124                             limit : this.footer.pageSize
10125                         },
10126                         add : true
10127                     });
10128                 }
10129             }
10130             
10131         }
10132     },
10133     onColumnSplitterMoved : function(i, diff)
10134     {
10135         this.userResized = true;
10136         
10137         var cm = this.colModel;
10138         
10139         var w = this.getHeaderIndex(i).getWidth() + diff;
10140         
10141         
10142         cm.setColumnWidth(i, w, true);
10143         this.initCSS();
10144         //var cid = cm.getColumnId(i); << not used in this version?
10145        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10146         
10147         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10148         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10149         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10150 */
10151         //this.updateSplitters();
10152         //this.layout(); << ??
10153         this.fireEvent("columnresize", i, w);
10154     },
10155     onHeaderChange : function()
10156     {
10157         var header = this.renderHeader();
10158         var table = this.el.select('table', true).first();
10159         
10160         this.headEl.remove();
10161         this.headEl = table.createChild(header, this.bodyEl, false);
10162         
10163         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10164             e.on('click', this.sort, this);
10165         }, this);
10166         
10167         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10168             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10169         }
10170         
10171     },
10172     
10173     onHiddenChange : function(colModel, colIndex, hidden)
10174     {
10175         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10176         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10177         
10178         this.CSS.updateRule(thSelector, "display", "");
10179         this.CSS.updateRule(tdSelector, "display", "");
10180         
10181         if(hidden){
10182             this.CSS.updateRule(thSelector, "display", "none");
10183             this.CSS.updateRule(tdSelector, "display", "none");
10184         }
10185         
10186         this.onHeaderChange();
10187         this.onLoad();
10188     },
10189     
10190     setColumnWidth: function(col_index, width)
10191     {
10192         // width = "md-2 xs-2..."
10193         if(!this.colModel.config[col_index]) {
10194             return;
10195         }
10196         
10197         var w = width.split(" ");
10198         
10199         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10200         
10201         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10202         
10203         
10204         for(var j = 0; j < w.length; j++) {
10205             
10206             if(!w[j]) {
10207                 continue;
10208             }
10209             
10210             var size_cls = w[j].split("-");
10211             
10212             if(!Number.isInteger(size_cls[1] * 1)) {
10213                 continue;
10214             }
10215             
10216             if(!this.colModel.config[col_index][size_cls[0]]) {
10217                 continue;
10218             }
10219             
10220             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10221                 continue;
10222             }
10223             
10224             h_row[0].classList.replace(
10225                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10226                 "col-"+size_cls[0]+"-"+size_cls[1]
10227             );
10228             
10229             for(var i = 0; i < rows.length; i++) {
10230                 
10231                 var size_cls = w[j].split("-");
10232                 
10233                 if(!Number.isInteger(size_cls[1] * 1)) {
10234                     continue;
10235                 }
10236                 
10237                 if(!this.colModel.config[col_index][size_cls[0]]) {
10238                     continue;
10239                 }
10240                 
10241                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10242                     continue;
10243                 }
10244                 
10245                 rows[i].classList.replace(
10246                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10247                     "col-"+size_cls[0]+"-"+size_cls[1]
10248                 );
10249             }
10250             
10251             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10252         }
10253     }
10254 });
10255
10256 // currently only used to find the split on drag.. 
10257 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10258
10259 /**
10260  * @depricated
10261 */
10262 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10263 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10264 /*
10265  * - LGPL
10266  *
10267  * table cell
10268  * 
10269  */
10270
10271 /**
10272  * @class Roo.bootstrap.TableCell
10273  * @extends Roo.bootstrap.Component
10274  * Bootstrap TableCell class
10275  * @cfg {String} html cell contain text
10276  * @cfg {String} cls cell class
10277  * @cfg {String} tag cell tag (td|th) default td
10278  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10279  * @cfg {String} align Aligns the content in a cell
10280  * @cfg {String} axis Categorizes cells
10281  * @cfg {String} bgcolor Specifies the background color of a cell
10282  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10283  * @cfg {Number} colspan Specifies the number of columns a cell should span
10284  * @cfg {String} headers Specifies one or more header cells a cell is related to
10285  * @cfg {Number} height Sets the height of a cell
10286  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10287  * @cfg {Number} rowspan Sets the number of rows a cell should span
10288  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10289  * @cfg {String} valign Vertical aligns the content in a cell
10290  * @cfg {Number} width Specifies the width of a cell
10291  * 
10292  * @constructor
10293  * Create a new TableCell
10294  * @param {Object} config The config object
10295  */
10296
10297 Roo.bootstrap.TableCell = function(config){
10298     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10299 };
10300
10301 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10302     
10303     html: false,
10304     cls: false,
10305     tag: false,
10306     abbr: false,
10307     align: false,
10308     axis: false,
10309     bgcolor: false,
10310     charoff: false,
10311     colspan: false,
10312     headers: false,
10313     height: false,
10314     nowrap: false,
10315     rowspan: false,
10316     scope: false,
10317     valign: false,
10318     width: false,
10319     
10320     
10321     getAutoCreate : function(){
10322         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10323         
10324         cfg = {
10325             tag: 'td'
10326         };
10327         
10328         if(this.tag){
10329             cfg.tag = this.tag;
10330         }
10331         
10332         if (this.html) {
10333             cfg.html=this.html
10334         }
10335         if (this.cls) {
10336             cfg.cls=this.cls
10337         }
10338         if (this.abbr) {
10339             cfg.abbr=this.abbr
10340         }
10341         if (this.align) {
10342             cfg.align=this.align
10343         }
10344         if (this.axis) {
10345             cfg.axis=this.axis
10346         }
10347         if (this.bgcolor) {
10348             cfg.bgcolor=this.bgcolor
10349         }
10350         if (this.charoff) {
10351             cfg.charoff=this.charoff
10352         }
10353         if (this.colspan) {
10354             cfg.colspan=this.colspan
10355         }
10356         if (this.headers) {
10357             cfg.headers=this.headers
10358         }
10359         if (this.height) {
10360             cfg.height=this.height
10361         }
10362         if (this.nowrap) {
10363             cfg.nowrap=this.nowrap
10364         }
10365         if (this.rowspan) {
10366             cfg.rowspan=this.rowspan
10367         }
10368         if (this.scope) {
10369             cfg.scope=this.scope
10370         }
10371         if (this.valign) {
10372             cfg.valign=this.valign
10373         }
10374         if (this.width) {
10375             cfg.width=this.width
10376         }
10377         
10378         
10379         return cfg;
10380     }
10381    
10382 });
10383
10384  
10385
10386  /*
10387  * - LGPL
10388  *
10389  * table row
10390  * 
10391  */
10392
10393 /**
10394  * @class Roo.bootstrap.TableRow
10395  * @extends Roo.bootstrap.Component
10396  * Bootstrap TableRow class
10397  * @cfg {String} cls row class
10398  * @cfg {String} align Aligns the content in a table row
10399  * @cfg {String} bgcolor Specifies a background color for a table row
10400  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10401  * @cfg {String} valign Vertical aligns the content in a table row
10402  * 
10403  * @constructor
10404  * Create a new TableRow
10405  * @param {Object} config The config object
10406  */
10407
10408 Roo.bootstrap.TableRow = function(config){
10409     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10410 };
10411
10412 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10413     
10414     cls: false,
10415     align: false,
10416     bgcolor: false,
10417     charoff: false,
10418     valign: false,
10419     
10420     getAutoCreate : function(){
10421         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10422         
10423         cfg = {
10424             tag: 'tr'
10425         };
10426             
10427         if(this.cls){
10428             cfg.cls = this.cls;
10429         }
10430         if(this.align){
10431             cfg.align = this.align;
10432         }
10433         if(this.bgcolor){
10434             cfg.bgcolor = this.bgcolor;
10435         }
10436         if(this.charoff){
10437             cfg.charoff = this.charoff;
10438         }
10439         if(this.valign){
10440             cfg.valign = this.valign;
10441         }
10442         
10443         return cfg;
10444     }
10445    
10446 });
10447
10448  
10449
10450  /*
10451  * - LGPL
10452  *
10453  * table body
10454  * 
10455  */
10456
10457 /**
10458  * @class Roo.bootstrap.TableBody
10459  * @extends Roo.bootstrap.Component
10460  * Bootstrap TableBody class
10461  * @cfg {String} cls element class
10462  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10463  * @cfg {String} align Aligns the content inside the element
10464  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10465  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10466  * 
10467  * @constructor
10468  * Create a new TableBody
10469  * @param {Object} config The config object
10470  */
10471
10472 Roo.bootstrap.TableBody = function(config){
10473     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10474 };
10475
10476 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10477     
10478     cls: false,
10479     tag: false,
10480     align: false,
10481     charoff: false,
10482     valign: false,
10483     
10484     getAutoCreate : function(){
10485         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10486         
10487         cfg = {
10488             tag: 'tbody'
10489         };
10490             
10491         if (this.cls) {
10492             cfg.cls=this.cls
10493         }
10494         if(this.tag){
10495             cfg.tag = this.tag;
10496         }
10497         
10498         if(this.align){
10499             cfg.align = this.align;
10500         }
10501         if(this.charoff){
10502             cfg.charoff = this.charoff;
10503         }
10504         if(this.valign){
10505             cfg.valign = this.valign;
10506         }
10507         
10508         return cfg;
10509     }
10510     
10511     
10512 //    initEvents : function()
10513 //    {
10514 //        
10515 //        if(!this.store){
10516 //            return;
10517 //        }
10518 //        
10519 //        this.store = Roo.factory(this.store, Roo.data);
10520 //        this.store.on('load', this.onLoad, this);
10521 //        
10522 //        this.store.load();
10523 //        
10524 //    },
10525 //    
10526 //    onLoad: function () 
10527 //    {   
10528 //        this.fireEvent('load', this);
10529 //    }
10530 //    
10531 //   
10532 });
10533
10534  
10535
10536  /*
10537  * Based on:
10538  * Ext JS Library 1.1.1
10539  * Copyright(c) 2006-2007, Ext JS, LLC.
10540  *
10541  * Originally Released Under LGPL - original licence link has changed is not relivant.
10542  *
10543  * Fork - LGPL
10544  * <script type="text/javascript">
10545  */
10546
10547 // as we use this in bootstrap.
10548 Roo.namespace('Roo.form');
10549  /**
10550  * @class Roo.form.Action
10551  * Internal Class used to handle form actions
10552  * @constructor
10553  * @param {Roo.form.BasicForm} el The form element or its id
10554  * @param {Object} config Configuration options
10555  */
10556
10557  
10558  
10559 // define the action interface
10560 Roo.form.Action = function(form, options){
10561     this.form = form;
10562     this.options = options || {};
10563 };
10564 /**
10565  * Client Validation Failed
10566  * @const 
10567  */
10568 Roo.form.Action.CLIENT_INVALID = 'client';
10569 /**
10570  * Server Validation Failed
10571  * @const 
10572  */
10573 Roo.form.Action.SERVER_INVALID = 'server';
10574  /**
10575  * Connect to Server Failed
10576  * @const 
10577  */
10578 Roo.form.Action.CONNECT_FAILURE = 'connect';
10579 /**
10580  * Reading Data from Server Failed
10581  * @const 
10582  */
10583 Roo.form.Action.LOAD_FAILURE = 'load';
10584
10585 Roo.form.Action.prototype = {
10586     type : 'default',
10587     failureType : undefined,
10588     response : undefined,
10589     result : undefined,
10590
10591     // interface method
10592     run : function(options){
10593
10594     },
10595
10596     // interface method
10597     success : function(response){
10598
10599     },
10600
10601     // interface method
10602     handleResponse : function(response){
10603
10604     },
10605
10606     // default connection failure
10607     failure : function(response){
10608         
10609         this.response = response;
10610         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10611         this.form.afterAction(this, false);
10612     },
10613
10614     processResponse : function(response){
10615         this.response = response;
10616         if(!response.responseText){
10617             return true;
10618         }
10619         this.result = this.handleResponse(response);
10620         return this.result;
10621     },
10622
10623     // utility functions used internally
10624     getUrl : function(appendParams){
10625         var url = this.options.url || this.form.url || this.form.el.dom.action;
10626         if(appendParams){
10627             var p = this.getParams();
10628             if(p){
10629                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10630             }
10631         }
10632         return url;
10633     },
10634
10635     getMethod : function(){
10636         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10637     },
10638
10639     getParams : function(){
10640         var bp = this.form.baseParams;
10641         var p = this.options.params;
10642         if(p){
10643             if(typeof p == "object"){
10644                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10645             }else if(typeof p == 'string' && bp){
10646                 p += '&' + Roo.urlEncode(bp);
10647             }
10648         }else if(bp){
10649             p = Roo.urlEncode(bp);
10650         }
10651         return p;
10652     },
10653
10654     createCallback : function(){
10655         return {
10656             success: this.success,
10657             failure: this.failure,
10658             scope: this,
10659             timeout: (this.form.timeout*1000),
10660             upload: this.form.fileUpload ? this.success : undefined
10661         };
10662     }
10663 };
10664
10665 Roo.form.Action.Submit = function(form, options){
10666     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10667 };
10668
10669 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10670     type : 'submit',
10671
10672     haveProgress : false,
10673     uploadComplete : false,
10674     
10675     // uploadProgress indicator.
10676     uploadProgress : function()
10677     {
10678         if (!this.form.progressUrl) {
10679             return;
10680         }
10681         
10682         if (!this.haveProgress) {
10683             Roo.MessageBox.progress("Uploading", "Uploading");
10684         }
10685         if (this.uploadComplete) {
10686            Roo.MessageBox.hide();
10687            return;
10688         }
10689         
10690         this.haveProgress = true;
10691    
10692         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10693         
10694         var c = new Roo.data.Connection();
10695         c.request({
10696             url : this.form.progressUrl,
10697             params: {
10698                 id : uid
10699             },
10700             method: 'GET',
10701             success : function(req){
10702                //console.log(data);
10703                 var rdata = false;
10704                 var edata;
10705                 try  {
10706                    rdata = Roo.decode(req.responseText)
10707                 } catch (e) {
10708                     Roo.log("Invalid data from server..");
10709                     Roo.log(edata);
10710                     return;
10711                 }
10712                 if (!rdata || !rdata.success) {
10713                     Roo.log(rdata);
10714                     Roo.MessageBox.alert(Roo.encode(rdata));
10715                     return;
10716                 }
10717                 var data = rdata.data;
10718                 
10719                 if (this.uploadComplete) {
10720                    Roo.MessageBox.hide();
10721                    return;
10722                 }
10723                    
10724                 if (data){
10725                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10726                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10727                     );
10728                 }
10729                 this.uploadProgress.defer(2000,this);
10730             },
10731        
10732             failure: function(data) {
10733                 Roo.log('progress url failed ');
10734                 Roo.log(data);
10735             },
10736             scope : this
10737         });
10738            
10739     },
10740     
10741     
10742     run : function()
10743     {
10744         // run get Values on the form, so it syncs any secondary forms.
10745         this.form.getValues();
10746         
10747         var o = this.options;
10748         var method = this.getMethod();
10749         var isPost = method == 'POST';
10750         if(o.clientValidation === false || this.form.isValid()){
10751             
10752             if (this.form.progressUrl) {
10753                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10754                     (new Date() * 1) + '' + Math.random());
10755                     
10756             } 
10757             
10758             
10759             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10760                 form:this.form.el.dom,
10761                 url:this.getUrl(!isPost),
10762                 method: method,
10763                 params:isPost ? this.getParams() : null,
10764                 isUpload: this.form.fileUpload,
10765                 formData : this.form.formData
10766             }));
10767             
10768             this.uploadProgress();
10769
10770         }else if (o.clientValidation !== false){ // client validation failed
10771             this.failureType = Roo.form.Action.CLIENT_INVALID;
10772             this.form.afterAction(this, false);
10773         }
10774     },
10775
10776     success : function(response)
10777     {
10778         this.uploadComplete= true;
10779         if (this.haveProgress) {
10780             Roo.MessageBox.hide();
10781         }
10782         
10783         
10784         var result = this.processResponse(response);
10785         if(result === true || result.success){
10786             this.form.afterAction(this, true);
10787             return;
10788         }
10789         if(result.errors){
10790             this.form.markInvalid(result.errors);
10791             this.failureType = Roo.form.Action.SERVER_INVALID;
10792         }
10793         this.form.afterAction(this, false);
10794     },
10795     failure : function(response)
10796     {
10797         this.uploadComplete= true;
10798         if (this.haveProgress) {
10799             Roo.MessageBox.hide();
10800         }
10801         
10802         this.response = response;
10803         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10804         this.form.afterAction(this, false);
10805     },
10806     
10807     handleResponse : function(response){
10808         if(this.form.errorReader){
10809             var rs = this.form.errorReader.read(response);
10810             var errors = [];
10811             if(rs.records){
10812                 for(var i = 0, len = rs.records.length; i < len; i++) {
10813                     var r = rs.records[i];
10814                     errors[i] = r.data;
10815                 }
10816             }
10817             if(errors.length < 1){
10818                 errors = null;
10819             }
10820             return {
10821                 success : rs.success,
10822                 errors : errors
10823             };
10824         }
10825         var ret = false;
10826         try {
10827             ret = Roo.decode(response.responseText);
10828         } catch (e) {
10829             ret = {
10830                 success: false,
10831                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10832                 errors : []
10833             };
10834         }
10835         return ret;
10836         
10837     }
10838 });
10839
10840
10841 Roo.form.Action.Load = function(form, options){
10842     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10843     this.reader = this.form.reader;
10844 };
10845
10846 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10847     type : 'load',
10848
10849     run : function(){
10850         
10851         Roo.Ajax.request(Roo.apply(
10852                 this.createCallback(), {
10853                     method:this.getMethod(),
10854                     url:this.getUrl(false),
10855                     params:this.getParams()
10856         }));
10857     },
10858
10859     success : function(response){
10860         
10861         var result = this.processResponse(response);
10862         if(result === true || !result.success || !result.data){
10863             this.failureType = Roo.form.Action.LOAD_FAILURE;
10864             this.form.afterAction(this, false);
10865             return;
10866         }
10867         this.form.clearInvalid();
10868         this.form.setValues(result.data);
10869         this.form.afterAction(this, true);
10870     },
10871
10872     handleResponse : function(response){
10873         if(this.form.reader){
10874             var rs = this.form.reader.read(response);
10875             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10876             return {
10877                 success : rs.success,
10878                 data : data
10879             };
10880         }
10881         return Roo.decode(response.responseText);
10882     }
10883 });
10884
10885 Roo.form.Action.ACTION_TYPES = {
10886     'load' : Roo.form.Action.Load,
10887     'submit' : Roo.form.Action.Submit
10888 };/*
10889  * - LGPL
10890  *
10891  * form
10892  *
10893  */
10894
10895 /**
10896  * @class Roo.bootstrap.Form
10897  * @extends Roo.bootstrap.Component
10898  * Bootstrap Form class
10899  * @cfg {String} method  GET | POST (default POST)
10900  * @cfg {String} labelAlign top | left (default top)
10901  * @cfg {String} align left  | right - for navbars
10902  * @cfg {Boolean} loadMask load mask when submit (default true)
10903
10904  *
10905  * @constructor
10906  * Create a new Form
10907  * @param {Object} config The config object
10908  */
10909
10910
10911 Roo.bootstrap.Form = function(config){
10912     
10913     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10914     
10915     Roo.bootstrap.Form.popover.apply();
10916     
10917     this.addEvents({
10918         /**
10919          * @event clientvalidation
10920          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10921          * @param {Form} this
10922          * @param {Boolean} valid true if the form has passed client-side validation
10923          */
10924         clientvalidation: true,
10925         /**
10926          * @event beforeaction
10927          * Fires before any action is performed. Return false to cancel the action.
10928          * @param {Form} this
10929          * @param {Action} action The action to be performed
10930          */
10931         beforeaction: true,
10932         /**
10933          * @event actionfailed
10934          * Fires when an action fails.
10935          * @param {Form} this
10936          * @param {Action} action The action that failed
10937          */
10938         actionfailed : true,
10939         /**
10940          * @event actioncomplete
10941          * Fires when an action is completed.
10942          * @param {Form} this
10943          * @param {Action} action The action that completed
10944          */
10945         actioncomplete : true
10946     });
10947 };
10948
10949 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10950
10951      /**
10952      * @cfg {String} method
10953      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10954      */
10955     method : 'POST',
10956     /**
10957      * @cfg {String} url
10958      * The URL to use for form actions if one isn't supplied in the action options.
10959      */
10960     /**
10961      * @cfg {Boolean} fileUpload
10962      * Set to true if this form is a file upload.
10963      */
10964
10965     /**
10966      * @cfg {Object} baseParams
10967      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10968      */
10969
10970     /**
10971      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10972      */
10973     timeout: 30,
10974     /**
10975      * @cfg {Sting} align (left|right) for navbar forms
10976      */
10977     align : 'left',
10978
10979     // private
10980     activeAction : null,
10981
10982     /**
10983      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10984      * element by passing it or its id or mask the form itself by passing in true.
10985      * @type Mixed
10986      */
10987     waitMsgTarget : false,
10988
10989     loadMask : true,
10990     
10991     /**
10992      * @cfg {Boolean} errorMask (true|false) default false
10993      */
10994     errorMask : false,
10995     
10996     /**
10997      * @cfg {Number} maskOffset Default 100
10998      */
10999     maskOffset : 100,
11000     
11001     /**
11002      * @cfg {Boolean} maskBody
11003      */
11004     maskBody : false,
11005
11006     getAutoCreate : function(){
11007
11008         var cfg = {
11009             tag: 'form',
11010             method : this.method || 'POST',
11011             id : this.id || Roo.id(),
11012             cls : ''
11013         };
11014         if (this.parent().xtype.match(/^Nav/)) {
11015             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11016
11017         }
11018
11019         if (this.labelAlign == 'left' ) {
11020             cfg.cls += ' form-horizontal';
11021         }
11022
11023
11024         return cfg;
11025     },
11026     initEvents : function()
11027     {
11028         this.el.on('submit', this.onSubmit, this);
11029         // this was added as random key presses on the form where triggering form submit.
11030         this.el.on('keypress', function(e) {
11031             if (e.getCharCode() != 13) {
11032                 return true;
11033             }
11034             // we might need to allow it for textareas.. and some other items.
11035             // check e.getTarget().
11036
11037             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11038                 return true;
11039             }
11040
11041             Roo.log("keypress blocked");
11042
11043             e.preventDefault();
11044             return false;
11045         });
11046         
11047     },
11048     // private
11049     onSubmit : function(e){
11050         e.stopEvent();
11051     },
11052
11053      /**
11054      * Returns true if client-side validation on the form is successful.
11055      * @return Boolean
11056      */
11057     isValid : function(){
11058         var items = this.getItems();
11059         var valid = true;
11060         var target = false;
11061         
11062         items.each(function(f){
11063             
11064             if(f.validate()){
11065                 return;
11066             }
11067             
11068             Roo.log('invalid field: ' + f.name);
11069             
11070             valid = false;
11071
11072             if(!target && f.el.isVisible(true)){
11073                 target = f;
11074             }
11075            
11076         });
11077         
11078         if(this.errorMask && !valid){
11079             Roo.bootstrap.Form.popover.mask(this, target);
11080         }
11081         
11082         return valid;
11083     },
11084     
11085     /**
11086      * Returns true if any fields in this form have changed since their original load.
11087      * @return Boolean
11088      */
11089     isDirty : function(){
11090         var dirty = false;
11091         var items = this.getItems();
11092         items.each(function(f){
11093            if(f.isDirty()){
11094                dirty = true;
11095                return false;
11096            }
11097            return true;
11098         });
11099         return dirty;
11100     },
11101      /**
11102      * Performs a predefined action (submit or load) or custom actions you define on this form.
11103      * @param {String} actionName The name of the action type
11104      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11105      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11106      * accept other config options):
11107      * <pre>
11108 Property          Type             Description
11109 ----------------  ---------------  ----------------------------------------------------------------------------------
11110 url               String           The url for the action (defaults to the form's url)
11111 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11112 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11113 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11114                                    validate the form on the client (defaults to false)
11115      * </pre>
11116      * @return {BasicForm} this
11117      */
11118     doAction : function(action, options){
11119         if(typeof action == 'string'){
11120             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11121         }
11122         if(this.fireEvent('beforeaction', this, action) !== false){
11123             this.beforeAction(action);
11124             action.run.defer(100, action);
11125         }
11126         return this;
11127     },
11128
11129     // private
11130     beforeAction : function(action){
11131         var o = action.options;
11132         
11133         if(this.loadMask){
11134             
11135             if(this.maskBody){
11136                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11137             } else {
11138                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11139             }
11140         }
11141         // not really supported yet.. ??
11142
11143         //if(this.waitMsgTarget === true){
11144         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145         //}else if(this.waitMsgTarget){
11146         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11147         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148         //}else {
11149         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11150        // }
11151
11152     },
11153
11154     // private
11155     afterAction : function(action, success){
11156         this.activeAction = null;
11157         var o = action.options;
11158
11159         if(this.loadMask){
11160             
11161             if(this.maskBody){
11162                 Roo.get(document.body).unmask();
11163             } else {
11164                 this.el.unmask();
11165             }
11166         }
11167         
11168         //if(this.waitMsgTarget === true){
11169 //            this.el.unmask();
11170         //}else if(this.waitMsgTarget){
11171         //    this.waitMsgTarget.unmask();
11172         //}else{
11173         //    Roo.MessageBox.updateProgress(1);
11174         //    Roo.MessageBox.hide();
11175        // }
11176         //
11177         if(success){
11178             if(o.reset){
11179                 this.reset();
11180             }
11181             Roo.callback(o.success, o.scope, [this, action]);
11182             this.fireEvent('actioncomplete', this, action);
11183
11184         }else{
11185
11186             // failure condition..
11187             // we have a scenario where updates need confirming.
11188             // eg. if a locking scenario exists..
11189             // we look for { errors : { needs_confirm : true }} in the response.
11190             if (
11191                 (typeof(action.result) != 'undefined')  &&
11192                 (typeof(action.result.errors) != 'undefined')  &&
11193                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11194            ){
11195                 var _t = this;
11196                 Roo.log("not supported yet");
11197                  /*
11198
11199                 Roo.MessageBox.confirm(
11200                     "Change requires confirmation",
11201                     action.result.errorMsg,
11202                     function(r) {
11203                         if (r != 'yes') {
11204                             return;
11205                         }
11206                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11207                     }
11208
11209                 );
11210                 */
11211
11212
11213                 return;
11214             }
11215
11216             Roo.callback(o.failure, o.scope, [this, action]);
11217             // show an error message if no failed handler is set..
11218             if (!this.hasListener('actionfailed')) {
11219                 Roo.log("need to add dialog support");
11220                 /*
11221                 Roo.MessageBox.alert("Error",
11222                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11223                         action.result.errorMsg :
11224                         "Saving Failed, please check your entries or try again"
11225                 );
11226                 */
11227             }
11228
11229             this.fireEvent('actionfailed', this, action);
11230         }
11231
11232     },
11233     /**
11234      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11235      * @param {String} id The value to search for
11236      * @return Field
11237      */
11238     findField : function(id){
11239         var items = this.getItems();
11240         var field = items.get(id);
11241         if(!field){
11242              items.each(function(f){
11243                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11244                     field = f;
11245                     return false;
11246                 }
11247                 return true;
11248             });
11249         }
11250         return field || null;
11251     },
11252      /**
11253      * Mark fields in this form invalid in bulk.
11254      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11255      * @return {BasicForm} this
11256      */
11257     markInvalid : function(errors){
11258         if(errors instanceof Array){
11259             for(var i = 0, len = errors.length; i < len; i++){
11260                 var fieldError = errors[i];
11261                 var f = this.findField(fieldError.id);
11262                 if(f){
11263                     f.markInvalid(fieldError.msg);
11264                 }
11265             }
11266         }else{
11267             var field, id;
11268             for(id in errors){
11269                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11270                     field.markInvalid(errors[id]);
11271                 }
11272             }
11273         }
11274         //Roo.each(this.childForms || [], function (f) {
11275         //    f.markInvalid(errors);
11276         //});
11277
11278         return this;
11279     },
11280
11281     /**
11282      * Set values for fields in this form in bulk.
11283      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11284      * @return {BasicForm} this
11285      */
11286     setValues : function(values){
11287         if(values instanceof Array){ // array of objects
11288             for(var i = 0, len = values.length; i < len; i++){
11289                 var v = values[i];
11290                 var f = this.findField(v.id);
11291                 if(f){
11292                     f.setValue(v.value);
11293                     if(this.trackResetOnLoad){
11294                         f.originalValue = f.getValue();
11295                     }
11296                 }
11297             }
11298         }else{ // object hash
11299             var field, id;
11300             for(id in values){
11301                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11302
11303                     if (field.setFromData &&
11304                         field.valueField &&
11305                         field.displayField &&
11306                         // combos' with local stores can
11307                         // be queried via setValue()
11308                         // to set their value..
11309                         (field.store && !field.store.isLocal)
11310                         ) {
11311                         // it's a combo
11312                         var sd = { };
11313                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11314                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11315                         field.setFromData(sd);
11316
11317                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11318                         
11319                         field.setFromData(values);
11320                         
11321                     } else {
11322                         field.setValue(values[id]);
11323                     }
11324
11325
11326                     if(this.trackResetOnLoad){
11327                         field.originalValue = field.getValue();
11328                     }
11329                 }
11330             }
11331         }
11332
11333         //Roo.each(this.childForms || [], function (f) {
11334         //    f.setValues(values);
11335         //});
11336
11337         return this;
11338     },
11339
11340     /**
11341      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11342      * they are returned as an array.
11343      * @param {Boolean} asString
11344      * @return {Object}
11345      */
11346     getValues : function(asString){
11347         //if (this.childForms) {
11348             // copy values from the child forms
11349         //    Roo.each(this.childForms, function (f) {
11350         //        this.setValues(f.getValues());
11351         //    }, this);
11352         //}
11353
11354
11355
11356         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11357         if(asString === true){
11358             return fs;
11359         }
11360         return Roo.urlDecode(fs);
11361     },
11362
11363     /**
11364      * Returns the fields in this form as an object with key/value pairs.
11365      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11366      * @return {Object}
11367      */
11368     getFieldValues : function(with_hidden)
11369     {
11370         var items = this.getItems();
11371         var ret = {};
11372         items.each(function(f){
11373             
11374             if (!f.getName()) {
11375                 return;
11376             }
11377             
11378             var v = f.getValue();
11379             
11380             if (f.inputType =='radio') {
11381                 if (typeof(ret[f.getName()]) == 'undefined') {
11382                     ret[f.getName()] = ''; // empty..
11383                 }
11384
11385                 if (!f.el.dom.checked) {
11386                     return;
11387
11388                 }
11389                 v = f.el.dom.value;
11390
11391             }
11392             
11393             if(f.xtype == 'MoneyField'){
11394                 ret[f.currencyName] = f.getCurrency();
11395             }
11396
11397             // not sure if this supported any more..
11398             if ((typeof(v) == 'object') && f.getRawValue) {
11399                 v = f.getRawValue() ; // dates..
11400             }
11401             // combo boxes where name != hiddenName...
11402             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11403                 ret[f.name] = f.getRawValue();
11404             }
11405             ret[f.getName()] = v;
11406         });
11407
11408         return ret;
11409     },
11410
11411     /**
11412      * Clears all invalid messages in this form.
11413      * @return {BasicForm} this
11414      */
11415     clearInvalid : function(){
11416         var items = this.getItems();
11417
11418         items.each(function(f){
11419            f.clearInvalid();
11420         });
11421
11422         return this;
11423     },
11424
11425     /**
11426      * Resets this form.
11427      * @return {BasicForm} this
11428      */
11429     reset : function(){
11430         var items = this.getItems();
11431         items.each(function(f){
11432             f.reset();
11433         });
11434
11435         Roo.each(this.childForms || [], function (f) {
11436             f.reset();
11437         });
11438
11439
11440         return this;
11441     },
11442     
11443     getItems : function()
11444     {
11445         var r=new Roo.util.MixedCollection(false, function(o){
11446             return o.id || (o.id = Roo.id());
11447         });
11448         var iter = function(el) {
11449             if (el.inputEl) {
11450                 r.add(el);
11451             }
11452             if (!el.items) {
11453                 return;
11454             }
11455             Roo.each(el.items,function(e) {
11456                 iter(e);
11457             });
11458         };
11459
11460         iter(this);
11461         return r;
11462     },
11463     
11464     hideFields : function(items)
11465     {
11466         Roo.each(items, function(i){
11467             
11468             var f = this.findField(i);
11469             
11470             if(!f){
11471                 return;
11472             }
11473             
11474             f.hide();
11475             
11476         }, this);
11477     },
11478     
11479     showFields : function(items)
11480     {
11481         Roo.each(items, function(i){
11482             
11483             var f = this.findField(i);
11484             
11485             if(!f){
11486                 return;
11487             }
11488             
11489             f.show();
11490             
11491         }, this);
11492     }
11493
11494 });
11495
11496 Roo.apply(Roo.bootstrap.Form, {
11497     
11498     popover : {
11499         
11500         padding : 5,
11501         
11502         isApplied : false,
11503         
11504         isMasked : false,
11505         
11506         form : false,
11507         
11508         target : false,
11509         
11510         toolTip : false,
11511         
11512         intervalID : false,
11513         
11514         maskEl : false,
11515         
11516         apply : function()
11517         {
11518             if(this.isApplied){
11519                 return;
11520             }
11521             
11522             this.maskEl = {
11523                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11524                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11525                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11526                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11527             };
11528             
11529             this.maskEl.top.enableDisplayMode("block");
11530             this.maskEl.left.enableDisplayMode("block");
11531             this.maskEl.bottom.enableDisplayMode("block");
11532             this.maskEl.right.enableDisplayMode("block");
11533             
11534             this.toolTip = new Roo.bootstrap.Tooltip({
11535                 cls : 'roo-form-error-popover',
11536                 alignment : {
11537                     'left' : ['r-l', [-2,0], 'right'],
11538                     'right' : ['l-r', [2,0], 'left'],
11539                     'bottom' : ['tl-bl', [0,2], 'top'],
11540                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11541                 }
11542             });
11543             
11544             this.toolTip.render(Roo.get(document.body));
11545
11546             this.toolTip.el.enableDisplayMode("block");
11547             
11548             Roo.get(document.body).on('click', function(){
11549                 this.unmask();
11550             }, this);
11551             
11552             Roo.get(document.body).on('touchstart', function(){
11553                 this.unmask();
11554             }, this);
11555             
11556             this.isApplied = true
11557         },
11558         
11559         mask : function(form, target)
11560         {
11561             this.form = form;
11562             
11563             this.target = target;
11564             
11565             if(!this.form.errorMask || !target.el){
11566                 return;
11567             }
11568             
11569             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11570             
11571             Roo.log(scrollable);
11572             
11573             var ot = this.target.el.calcOffsetsTo(scrollable);
11574             
11575             var scrollTo = ot[1] - this.form.maskOffset;
11576             
11577             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11578             
11579             scrollable.scrollTo('top', scrollTo);
11580             
11581             var box = this.target.el.getBox();
11582             Roo.log(box);
11583             var zIndex = Roo.bootstrap.Modal.zIndex++;
11584
11585             
11586             this.maskEl.top.setStyle('position', 'absolute');
11587             this.maskEl.top.setStyle('z-index', zIndex);
11588             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11589             this.maskEl.top.setLeft(0);
11590             this.maskEl.top.setTop(0);
11591             this.maskEl.top.show();
11592             
11593             this.maskEl.left.setStyle('position', 'absolute');
11594             this.maskEl.left.setStyle('z-index', zIndex);
11595             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11596             this.maskEl.left.setLeft(0);
11597             this.maskEl.left.setTop(box.y - this.padding);
11598             this.maskEl.left.show();
11599
11600             this.maskEl.bottom.setStyle('position', 'absolute');
11601             this.maskEl.bottom.setStyle('z-index', zIndex);
11602             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11603             this.maskEl.bottom.setLeft(0);
11604             this.maskEl.bottom.setTop(box.bottom + this.padding);
11605             this.maskEl.bottom.show();
11606
11607             this.maskEl.right.setStyle('position', 'absolute');
11608             this.maskEl.right.setStyle('z-index', zIndex);
11609             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11610             this.maskEl.right.setLeft(box.right + this.padding);
11611             this.maskEl.right.setTop(box.y - this.padding);
11612             this.maskEl.right.show();
11613
11614             this.toolTip.bindEl = this.target.el;
11615
11616             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11617
11618             var tip = this.target.blankText;
11619
11620             if(this.target.getValue() !== '' ) {
11621                 
11622                 if (this.target.invalidText.length) {
11623                     tip = this.target.invalidText;
11624                 } else if (this.target.regexText.length){
11625                     tip = this.target.regexText;
11626                 }
11627             }
11628
11629             this.toolTip.show(tip);
11630
11631             this.intervalID = window.setInterval(function() {
11632                 Roo.bootstrap.Form.popover.unmask();
11633             }, 10000);
11634
11635             window.onwheel = function(){ return false;};
11636             
11637             (function(){ this.isMasked = true; }).defer(500, this);
11638             
11639         },
11640         
11641         unmask : function()
11642         {
11643             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11644                 return;
11645             }
11646             
11647             this.maskEl.top.setStyle('position', 'absolute');
11648             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11649             this.maskEl.top.hide();
11650
11651             this.maskEl.left.setStyle('position', 'absolute');
11652             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11653             this.maskEl.left.hide();
11654
11655             this.maskEl.bottom.setStyle('position', 'absolute');
11656             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11657             this.maskEl.bottom.hide();
11658
11659             this.maskEl.right.setStyle('position', 'absolute');
11660             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11661             this.maskEl.right.hide();
11662             
11663             this.toolTip.hide();
11664             
11665             this.toolTip.el.hide();
11666             
11667             window.onwheel = function(){ return true;};
11668             
11669             if(this.intervalID){
11670                 window.clearInterval(this.intervalID);
11671                 this.intervalID = false;
11672             }
11673             
11674             this.isMasked = false;
11675             
11676         }
11677         
11678     }
11679     
11680 });
11681
11682 /*
11683  * Based on:
11684  * Ext JS Library 1.1.1
11685  * Copyright(c) 2006-2007, Ext JS, LLC.
11686  *
11687  * Originally Released Under LGPL - original licence link has changed is not relivant.
11688  *
11689  * Fork - LGPL
11690  * <script type="text/javascript">
11691  */
11692 /**
11693  * @class Roo.form.VTypes
11694  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11695  * @singleton
11696  */
11697 Roo.form.VTypes = function(){
11698     // closure these in so they are only created once.
11699     var alpha = /^[a-zA-Z_]+$/;
11700     var alphanum = /^[a-zA-Z0-9_]+$/;
11701     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11702     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11703
11704     // All these messages and functions are configurable
11705     return {
11706         /**
11707          * The function used to validate email addresses
11708          * @param {String} value The email address
11709          */
11710         'email' : function(v){
11711             return email.test(v);
11712         },
11713         /**
11714          * The error text to display when the email validation function returns false
11715          * @type String
11716          */
11717         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11718         /**
11719          * The keystroke filter mask to be applied on email input
11720          * @type RegExp
11721          */
11722         'emailMask' : /[a-z0-9_\.\-@]/i,
11723
11724         /**
11725          * The function used to validate URLs
11726          * @param {String} value The URL
11727          */
11728         'url' : function(v){
11729             return url.test(v);
11730         },
11731         /**
11732          * The error text to display when the url validation function returns false
11733          * @type String
11734          */
11735         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11736         
11737         /**
11738          * The function used to validate alpha values
11739          * @param {String} value The value
11740          */
11741         'alpha' : function(v){
11742             return alpha.test(v);
11743         },
11744         /**
11745          * The error text to display when the alpha validation function returns false
11746          * @type String
11747          */
11748         'alphaText' : 'This field should only contain letters and _',
11749         /**
11750          * The keystroke filter mask to be applied on alpha input
11751          * @type RegExp
11752          */
11753         'alphaMask' : /[a-z_]/i,
11754
11755         /**
11756          * The function used to validate alphanumeric values
11757          * @param {String} value The value
11758          */
11759         'alphanum' : function(v){
11760             return alphanum.test(v);
11761         },
11762         /**
11763          * The error text to display when the alphanumeric validation function returns false
11764          * @type String
11765          */
11766         'alphanumText' : 'This field should only contain letters, numbers and _',
11767         /**
11768          * The keystroke filter mask to be applied on alphanumeric input
11769          * @type RegExp
11770          */
11771         'alphanumMask' : /[a-z0-9_]/i
11772     };
11773 }();/*
11774  * - LGPL
11775  *
11776  * Input
11777  * 
11778  */
11779
11780 /**
11781  * @class Roo.bootstrap.Input
11782  * @extends Roo.bootstrap.Component
11783  * Bootstrap Input class
11784  * @cfg {Boolean} disabled is it disabled
11785  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11786  * @cfg {String} name name of the input
11787  * @cfg {string} fieldLabel - the label associated
11788  * @cfg {string} placeholder - placeholder to put in text.
11789  * @cfg {string}  before - input group add on before
11790  * @cfg {string} after - input group add on after
11791  * @cfg {string} size - (lg|sm) or leave empty..
11792  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11793  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11794  * @cfg {Number} md colspan out of 12 for computer-sized screens
11795  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11796  * @cfg {string} value default value of the input
11797  * @cfg {Number} labelWidth set the width of label 
11798  * @cfg {Number} labellg set the width of label (1-12)
11799  * @cfg {Number} labelmd set the width of label (1-12)
11800  * @cfg {Number} labelsm set the width of label (1-12)
11801  * @cfg {Number} labelxs set the width of label (1-12)
11802  * @cfg {String} labelAlign (top|left)
11803  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11804  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11805  * @cfg {String} indicatorpos (left|right) default left
11806  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11807  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11808  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11809
11810  * @cfg {String} align (left|center|right) Default left
11811  * @cfg {Boolean} forceFeedback (true|false) Default false
11812  * 
11813  * @constructor
11814  * Create a new Input
11815  * @param {Object} config The config object
11816  */
11817
11818 Roo.bootstrap.Input = function(config){
11819     
11820     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11821     
11822     this.addEvents({
11823         /**
11824          * @event focus
11825          * Fires when this field receives input focus.
11826          * @param {Roo.form.Field} this
11827          */
11828         focus : true,
11829         /**
11830          * @event blur
11831          * Fires when this field loses input focus.
11832          * @param {Roo.form.Field} this
11833          */
11834         blur : true,
11835         /**
11836          * @event specialkey
11837          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11838          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11839          * @param {Roo.form.Field} this
11840          * @param {Roo.EventObject} e The event object
11841          */
11842         specialkey : true,
11843         /**
11844          * @event change
11845          * Fires just before the field blurs if the field value has changed.
11846          * @param {Roo.form.Field} this
11847          * @param {Mixed} newValue The new value
11848          * @param {Mixed} oldValue The original value
11849          */
11850         change : true,
11851         /**
11852          * @event invalid
11853          * Fires after the field has been marked as invalid.
11854          * @param {Roo.form.Field} this
11855          * @param {String} msg The validation message
11856          */
11857         invalid : true,
11858         /**
11859          * @event valid
11860          * Fires after the field has been validated with no errors.
11861          * @param {Roo.form.Field} this
11862          */
11863         valid : true,
11864          /**
11865          * @event keyup
11866          * Fires after the key up
11867          * @param {Roo.form.Field} this
11868          * @param {Roo.EventObject}  e The event Object
11869          */
11870         keyup : true,
11871         /**
11872          * @event paste
11873          * Fires after the user pastes into input
11874          * @param {Roo.form.Field} this
11875          * @param {Roo.EventObject}  e The event Object
11876          */
11877         paste : true
11878     });
11879 };
11880
11881 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11882      /**
11883      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11884       automatic validation (defaults to "keyup").
11885      */
11886     validationEvent : "keyup",
11887      /**
11888      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11889      */
11890     validateOnBlur : true,
11891     /**
11892      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11893      */
11894     validationDelay : 250,
11895      /**
11896      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11897      */
11898     focusClass : "x-form-focus",  // not needed???
11899     
11900        
11901     /**
11902      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11903      */
11904     invalidClass : "has-warning",
11905     
11906     /**
11907      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11908      */
11909     validClass : "has-success",
11910     
11911     /**
11912      * @cfg {Boolean} hasFeedback (true|false) default true
11913      */
11914     hasFeedback : true,
11915     
11916     /**
11917      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11918      */
11919     invalidFeedbackClass : "glyphicon-warning-sign",
11920     
11921     /**
11922      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11923      */
11924     validFeedbackClass : "glyphicon-ok",
11925     
11926     /**
11927      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11928      */
11929     selectOnFocus : false,
11930     
11931      /**
11932      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11933      */
11934     maskRe : null,
11935        /**
11936      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11937      */
11938     vtype : null,
11939     
11940       /**
11941      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11942      */
11943     disableKeyFilter : false,
11944     
11945        /**
11946      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11947      */
11948     disabled : false,
11949      /**
11950      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11951      */
11952     allowBlank : true,
11953     /**
11954      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11955      */
11956     blankText : "Please complete this mandatory field",
11957     
11958      /**
11959      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11960      */
11961     minLength : 0,
11962     /**
11963      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11964      */
11965     maxLength : Number.MAX_VALUE,
11966     /**
11967      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11968      */
11969     minLengthText : "The minimum length for this field is {0}",
11970     /**
11971      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11972      */
11973     maxLengthText : "The maximum length for this field is {0}",
11974   
11975     
11976     /**
11977      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11978      * If available, this function will be called only after the basic validators all return true, and will be passed the
11979      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11980      */
11981     validator : null,
11982     /**
11983      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11984      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11985      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11986      */
11987     regex : null,
11988     /**
11989      * @cfg {String} regexText -- Depricated - use Invalid Text
11990      */
11991     regexText : "",
11992     
11993     /**
11994      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11995      */
11996     invalidText : "",
11997     
11998     
11999     
12000     autocomplete: false,
12001     
12002     
12003     fieldLabel : '',
12004     inputType : 'text',
12005     
12006     name : false,
12007     placeholder: false,
12008     before : false,
12009     after : false,
12010     size : false,
12011     hasFocus : false,
12012     preventMark: false,
12013     isFormField : true,
12014     value : '',
12015     labelWidth : 2,
12016     labelAlign : false,
12017     readOnly : false,
12018     align : false,
12019     formatedValue : false,
12020     forceFeedback : false,
12021     
12022     indicatorpos : 'left',
12023     
12024     labellg : 0,
12025     labelmd : 0,
12026     labelsm : 0,
12027     labelxs : 0,
12028     
12029     capture : '',
12030     accept : '',
12031     
12032     parentLabelAlign : function()
12033     {
12034         var parent = this;
12035         while (parent.parent()) {
12036             parent = parent.parent();
12037             if (typeof(parent.labelAlign) !='undefined') {
12038                 return parent.labelAlign;
12039             }
12040         }
12041         return 'left';
12042         
12043     },
12044     
12045     getAutoCreate : function()
12046     {
12047         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12048         
12049         var id = Roo.id();
12050         
12051         var cfg = {};
12052         
12053         if(this.inputType != 'hidden'){
12054             cfg.cls = 'form-group' //input-group
12055         }
12056         
12057         var input =  {
12058             tag: 'input',
12059             id : id,
12060             type : this.inputType,
12061             value : this.value,
12062             cls : 'form-control',
12063             placeholder : this.placeholder || '',
12064             autocomplete : this.autocomplete || 'new-password'
12065         };
12066         if (this.inputType == 'file') {
12067             input.style = 'overflow:hidden'; // why not in CSS?
12068         }
12069         
12070         if(this.capture.length){
12071             input.capture = this.capture;
12072         }
12073         
12074         if(this.accept.length){
12075             input.accept = this.accept + "/*";
12076         }
12077         
12078         if(this.align){
12079             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12080         }
12081         
12082         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12083             input.maxLength = this.maxLength;
12084         }
12085         
12086         if (this.disabled) {
12087             input.disabled=true;
12088         }
12089         
12090         if (this.readOnly) {
12091             input.readonly=true;
12092         }
12093         
12094         if (this.name) {
12095             input.name = this.name;
12096         }
12097         
12098         if (this.size) {
12099             input.cls += ' input-' + this.size;
12100         }
12101         
12102         var settings=this;
12103         ['xs','sm','md','lg'].map(function(size){
12104             if (settings[size]) {
12105                 cfg.cls += ' col-' + size + '-' + settings[size];
12106             }
12107         });
12108         
12109         var inputblock = input;
12110         
12111         var feedback = {
12112             tag: 'span',
12113             cls: 'glyphicon form-control-feedback'
12114         };
12115             
12116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12117             
12118             inputblock = {
12119                 cls : 'has-feedback',
12120                 cn :  [
12121                     input,
12122                     feedback
12123                 ] 
12124             };  
12125         }
12126         
12127         if (this.before || this.after) {
12128             
12129             inputblock = {
12130                 cls : 'input-group',
12131                 cn :  [] 
12132             };
12133             
12134             if (this.before && typeof(this.before) == 'string') {
12135                 
12136                 inputblock.cn.push({
12137                     tag :'span',
12138                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12139                     html : this.before
12140                 });
12141             }
12142             if (this.before && typeof(this.before) == 'object') {
12143                 this.before = Roo.factory(this.before);
12144                 
12145                 inputblock.cn.push({
12146                     tag :'span',
12147                     cls : 'roo-input-before input-group-prepend   input-group-' +
12148                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12149                 });
12150             }
12151             
12152             inputblock.cn.push(input);
12153             
12154             if (this.after && typeof(this.after) == 'string') {
12155                 inputblock.cn.push({
12156                     tag :'span',
12157                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12158                     html : this.after
12159                 });
12160             }
12161             if (this.after && typeof(this.after) == 'object') {
12162                 this.after = Roo.factory(this.after);
12163                 
12164                 inputblock.cn.push({
12165                     tag :'span',
12166                     cls : 'roo-input-after input-group-append  input-group-' +
12167                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12168                 });
12169             }
12170             
12171             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12172                 inputblock.cls += ' has-feedback';
12173                 inputblock.cn.push(feedback);
12174             }
12175         };
12176         var indicator = {
12177             tag : 'i',
12178             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12179             tooltip : 'This field is required'
12180         };
12181         if (this.allowBlank ) {
12182             indicator.style = this.allowBlank ? ' display:none' : '';
12183         }
12184         if (align ==='left' && this.fieldLabel.length) {
12185             
12186             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12187             
12188             cfg.cn = [
12189                 indicator,
12190                 {
12191                     tag: 'label',
12192                     'for' :  id,
12193                     cls : 'control-label col-form-label',
12194                     html : this.fieldLabel
12195
12196                 },
12197                 {
12198                     cls : "", 
12199                     cn: [
12200                         inputblock
12201                     ]
12202                 }
12203             ];
12204             
12205             var labelCfg = cfg.cn[1];
12206             var contentCfg = cfg.cn[2];
12207             
12208             if(this.indicatorpos == 'right'){
12209                 cfg.cn = [
12210                     {
12211                         tag: 'label',
12212                         'for' :  id,
12213                         cls : 'control-label col-form-label',
12214                         cn : [
12215                             {
12216                                 tag : 'span',
12217                                 html : this.fieldLabel
12218                             },
12219                             indicator
12220                         ]
12221                     },
12222                     {
12223                         cls : "",
12224                         cn: [
12225                             inputblock
12226                         ]
12227                     }
12228
12229                 ];
12230                 
12231                 labelCfg = cfg.cn[0];
12232                 contentCfg = cfg.cn[1];
12233             
12234             }
12235             
12236             if(this.labelWidth > 12){
12237                 labelCfg.style = "width: " + this.labelWidth + 'px';
12238             }
12239             
12240             if(this.labelWidth < 13 && this.labelmd == 0){
12241                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12242             }
12243             
12244             if(this.labellg > 0){
12245                 labelCfg.cls += ' col-lg-' + this.labellg;
12246                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12247             }
12248             
12249             if(this.labelmd > 0){
12250                 labelCfg.cls += ' col-md-' + this.labelmd;
12251                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12252             }
12253             
12254             if(this.labelsm > 0){
12255                 labelCfg.cls += ' col-sm-' + this.labelsm;
12256                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12257             }
12258             
12259             if(this.labelxs > 0){
12260                 labelCfg.cls += ' col-xs-' + this.labelxs;
12261                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12262             }
12263             
12264             
12265         } else if ( this.fieldLabel.length) {
12266                 
12267             
12268             
12269             cfg.cn = [
12270                 {
12271                     tag : 'i',
12272                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12273                     tooltip : 'This field is required',
12274                     style : this.allowBlank ? ' display:none' : '' 
12275                 },
12276                 {
12277                     tag: 'label',
12278                    //cls : 'input-group-addon',
12279                     html : this.fieldLabel
12280
12281                 },
12282
12283                inputblock
12284
12285            ];
12286            
12287            if(this.indicatorpos == 'right'){
12288        
12289                 cfg.cn = [
12290                     {
12291                         tag: 'label',
12292                        //cls : 'input-group-addon',
12293                         html : this.fieldLabel
12294
12295                     },
12296                     {
12297                         tag : 'i',
12298                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12299                         tooltip : 'This field is required',
12300                         style : this.allowBlank ? ' display:none' : '' 
12301                     },
12302
12303                    inputblock
12304
12305                ];
12306
12307             }
12308
12309         } else {
12310             
12311             cfg.cn = [
12312
12313                     inputblock
12314
12315             ];
12316                 
12317                 
12318         };
12319         
12320         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12321            cfg.cls += ' navbar-form';
12322         }
12323         
12324         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12325             // on BS4 we do this only if not form 
12326             cfg.cls += ' navbar-form';
12327             cfg.tag = 'li';
12328         }
12329         
12330         return cfg;
12331         
12332     },
12333     /**
12334      * return the real input element.
12335      */
12336     inputEl: function ()
12337     {
12338         return this.el.select('input.form-control',true).first();
12339     },
12340     
12341     tooltipEl : function()
12342     {
12343         return this.inputEl();
12344     },
12345     
12346     indicatorEl : function()
12347     {
12348         if (Roo.bootstrap.version == 4) {
12349             return false; // not enabled in v4 yet.
12350         }
12351         
12352         var indicator = this.el.select('i.roo-required-indicator',true).first();
12353         
12354         if(!indicator){
12355             return false;
12356         }
12357         
12358         return indicator;
12359         
12360     },
12361     
12362     setDisabled : function(v)
12363     {
12364         var i  = this.inputEl().dom;
12365         if (!v) {
12366             i.removeAttribute('disabled');
12367             return;
12368             
12369         }
12370         i.setAttribute('disabled','true');
12371     },
12372     initEvents : function()
12373     {
12374           
12375         this.inputEl().on("keydown" , this.fireKey,  this);
12376         this.inputEl().on("focus", this.onFocus,  this);
12377         this.inputEl().on("blur", this.onBlur,  this);
12378         
12379         this.inputEl().relayEvent('keyup', this);
12380         this.inputEl().relayEvent('paste', this);
12381         
12382         this.indicator = this.indicatorEl();
12383         
12384         if(this.indicator){
12385             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12386         }
12387  
12388         // reference to original value for reset
12389         this.originalValue = this.getValue();
12390         //Roo.form.TextField.superclass.initEvents.call(this);
12391         if(this.validationEvent == 'keyup'){
12392             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12393             this.inputEl().on('keyup', this.filterValidation, this);
12394         }
12395         else if(this.validationEvent !== false){
12396             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12397         }
12398         
12399         if(this.selectOnFocus){
12400             this.on("focus", this.preFocus, this);
12401             
12402         }
12403         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12404             this.inputEl().on("keypress", this.filterKeys, this);
12405         } else {
12406             this.inputEl().relayEvent('keypress', this);
12407         }
12408        /* if(this.grow){
12409             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12410             this.el.on("click", this.autoSize,  this);
12411         }
12412         */
12413         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12414             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12415         }
12416         
12417         if (typeof(this.before) == 'object') {
12418             this.before.render(this.el.select('.roo-input-before',true).first());
12419         }
12420         if (typeof(this.after) == 'object') {
12421             this.after.render(this.el.select('.roo-input-after',true).first());
12422         }
12423         
12424         this.inputEl().on('change', this.onChange, this);
12425         
12426     },
12427     filterValidation : function(e){
12428         if(!e.isNavKeyPress()){
12429             this.validationTask.delay(this.validationDelay);
12430         }
12431     },
12432      /**
12433      * Validates the field value
12434      * @return {Boolean} True if the value is valid, else false
12435      */
12436     validate : function(){
12437         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12438         if(this.disabled || this.validateValue(this.getRawValue())){
12439             this.markValid();
12440             return true;
12441         }
12442         
12443         this.markInvalid();
12444         return false;
12445     },
12446     
12447     
12448     /**
12449      * Validates a value according to the field's validation rules and marks the field as invalid
12450      * if the validation fails
12451      * @param {Mixed} value The value to validate
12452      * @return {Boolean} True if the value is valid, else false
12453      */
12454     validateValue : function(value)
12455     {
12456         if(this.getVisibilityEl().hasClass('hidden')){
12457             return true;
12458         }
12459         
12460         if(value.length < 1)  { // if it's blank
12461             if(this.allowBlank){
12462                 return true;
12463             }
12464             return false;
12465         }
12466         
12467         if(value.length < this.minLength){
12468             return false;
12469         }
12470         if(value.length > this.maxLength){
12471             return false;
12472         }
12473         if(this.vtype){
12474             var vt = Roo.form.VTypes;
12475             if(!vt[this.vtype](value, this)){
12476                 return false;
12477             }
12478         }
12479         if(typeof this.validator == "function"){
12480             var msg = this.validator(value);
12481             if(msg !== true){
12482                 return false;
12483             }
12484             if (typeof(msg) == 'string') {
12485                 this.invalidText = msg;
12486             }
12487         }
12488         
12489         if(this.regex && !this.regex.test(value)){
12490             return false;
12491         }
12492         
12493         return true;
12494     },
12495     
12496      // private
12497     fireKey : function(e){
12498         //Roo.log('field ' + e.getKey());
12499         if(e.isNavKeyPress()){
12500             this.fireEvent("specialkey", this, e);
12501         }
12502     },
12503     focus : function (selectText){
12504         if(this.rendered){
12505             this.inputEl().focus();
12506             if(selectText === true){
12507                 this.inputEl().dom.select();
12508             }
12509         }
12510         return this;
12511     } ,
12512     
12513     onFocus : function(){
12514         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12515            // this.el.addClass(this.focusClass);
12516         }
12517         if(!this.hasFocus){
12518             this.hasFocus = true;
12519             this.startValue = this.getValue();
12520             this.fireEvent("focus", this);
12521         }
12522     },
12523     
12524     beforeBlur : Roo.emptyFn,
12525
12526     
12527     // private
12528     onBlur : function(){
12529         this.beforeBlur();
12530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12531             //this.el.removeClass(this.focusClass);
12532         }
12533         this.hasFocus = false;
12534         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12535             this.validate();
12536         }
12537         var v = this.getValue();
12538         if(String(v) !== String(this.startValue)){
12539             this.fireEvent('change', this, v, this.startValue);
12540         }
12541         this.fireEvent("blur", this);
12542     },
12543     
12544     onChange : function(e)
12545     {
12546         var v = this.getValue();
12547         if(String(v) !== String(this.startValue)){
12548             this.fireEvent('change', this, v, this.startValue);
12549         }
12550         
12551     },
12552     
12553     /**
12554      * Resets the current field value to the originally loaded value and clears any validation messages
12555      */
12556     reset : function(){
12557         this.setValue(this.originalValue);
12558         this.validate();
12559     },
12560      /**
12561      * Returns the name of the field
12562      * @return {Mixed} name The name field
12563      */
12564     getName: function(){
12565         return this.name;
12566     },
12567      /**
12568      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12569      * @return {Mixed} value The field value
12570      */
12571     getValue : function(){
12572         
12573         var v = this.inputEl().getValue();
12574         
12575         return v;
12576     },
12577     /**
12578      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12579      * @return {Mixed} value The field value
12580      */
12581     getRawValue : function(){
12582         var v = this.inputEl().getValue();
12583         
12584         return v;
12585     },
12586     
12587     /**
12588      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12589      * @param {Mixed} value The value to set
12590      */
12591     setRawValue : function(v){
12592         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12593     },
12594     
12595     selectText : function(start, end){
12596         var v = this.getRawValue();
12597         if(v.length > 0){
12598             start = start === undefined ? 0 : start;
12599             end = end === undefined ? v.length : end;
12600             var d = this.inputEl().dom;
12601             if(d.setSelectionRange){
12602                 d.setSelectionRange(start, end);
12603             }else if(d.createTextRange){
12604                 var range = d.createTextRange();
12605                 range.moveStart("character", start);
12606                 range.moveEnd("character", v.length-end);
12607                 range.select();
12608             }
12609         }
12610     },
12611     
12612     /**
12613      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12614      * @param {Mixed} value The value to set
12615      */
12616     setValue : function(v){
12617         this.value = v;
12618         if(this.rendered){
12619             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12620             this.validate();
12621         }
12622     },
12623     
12624     /*
12625     processValue : function(value){
12626         if(this.stripCharsRe){
12627             var newValue = value.replace(this.stripCharsRe, '');
12628             if(newValue !== value){
12629                 this.setRawValue(newValue);
12630                 return newValue;
12631             }
12632         }
12633         return value;
12634     },
12635   */
12636     preFocus : function(){
12637         
12638         if(this.selectOnFocus){
12639             this.inputEl().dom.select();
12640         }
12641     },
12642     filterKeys : function(e){
12643         var k = e.getKey();
12644         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12645             return;
12646         }
12647         var c = e.getCharCode(), cc = String.fromCharCode(c);
12648         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12649             return;
12650         }
12651         if(!this.maskRe.test(cc)){
12652             e.stopEvent();
12653         }
12654     },
12655      /**
12656      * Clear any invalid styles/messages for this field
12657      */
12658     clearInvalid : function(){
12659         
12660         if(!this.el || this.preventMark){ // not rendered
12661             return;
12662         }
12663         
12664         
12665         this.el.removeClass([this.invalidClass, 'is-invalid']);
12666         
12667         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12668             
12669             var feedback = this.el.select('.form-control-feedback', true).first();
12670             
12671             if(feedback){
12672                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12673             }
12674             
12675         }
12676         
12677         if(this.indicator){
12678             this.indicator.removeClass('visible');
12679             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12680         }
12681         
12682         this.fireEvent('valid', this);
12683     },
12684     
12685      /**
12686      * Mark this field as valid
12687      */
12688     markValid : function()
12689     {
12690         if(!this.el  || this.preventMark){ // not rendered...
12691             return;
12692         }
12693         
12694         this.el.removeClass([this.invalidClass, this.validClass]);
12695         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12696
12697         var feedback = this.el.select('.form-control-feedback', true).first();
12698             
12699         if(feedback){
12700             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12701         }
12702         
12703         if(this.indicator){
12704             this.indicator.removeClass('visible');
12705             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12706         }
12707         
12708         if(this.disabled){
12709             return;
12710         }
12711         
12712            
12713         if(this.allowBlank && !this.getRawValue().length){
12714             return;
12715         }
12716         if (Roo.bootstrap.version == 3) {
12717             this.el.addClass(this.validClass);
12718         } else {
12719             this.inputEl().addClass('is-valid');
12720         }
12721
12722         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12723             
12724             var feedback = this.el.select('.form-control-feedback', true).first();
12725             
12726             if(feedback){
12727                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12728                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12729             }
12730             
12731         }
12732         
12733         this.fireEvent('valid', this);
12734     },
12735     
12736      /**
12737      * Mark this field as invalid
12738      * @param {String} msg The validation message
12739      */
12740     markInvalid : function(msg)
12741     {
12742         if(!this.el  || this.preventMark){ // not rendered
12743             return;
12744         }
12745         
12746         this.el.removeClass([this.invalidClass, this.validClass]);
12747         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12748         
12749         var feedback = this.el.select('.form-control-feedback', true).first();
12750             
12751         if(feedback){
12752             this.el.select('.form-control-feedback', true).first().removeClass(
12753                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12754         }
12755
12756         if(this.disabled){
12757             return;
12758         }
12759         
12760         if(this.allowBlank && !this.getRawValue().length){
12761             return;
12762         }
12763         
12764         if(this.indicator){
12765             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12766             this.indicator.addClass('visible');
12767         }
12768         if (Roo.bootstrap.version == 3) {
12769             this.el.addClass(this.invalidClass);
12770         } else {
12771             this.inputEl().addClass('is-invalid');
12772         }
12773         
12774         
12775         
12776         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12777             
12778             var feedback = this.el.select('.form-control-feedback', true).first();
12779             
12780             if(feedback){
12781                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12782                 
12783                 if(this.getValue().length || this.forceFeedback){
12784                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12785                 }
12786                 
12787             }
12788             
12789         }
12790         
12791         this.fireEvent('invalid', this, msg);
12792     },
12793     // private
12794     SafariOnKeyDown : function(event)
12795     {
12796         // this is a workaround for a password hang bug on chrome/ webkit.
12797         if (this.inputEl().dom.type != 'password') {
12798             return;
12799         }
12800         
12801         var isSelectAll = false;
12802         
12803         if(this.inputEl().dom.selectionEnd > 0){
12804             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12805         }
12806         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12807             event.preventDefault();
12808             this.setValue('');
12809             return;
12810         }
12811         
12812         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12813             
12814             event.preventDefault();
12815             // this is very hacky as keydown always get's upper case.
12816             //
12817             var cc = String.fromCharCode(event.getCharCode());
12818             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12819             
12820         }
12821     },
12822     adjustWidth : function(tag, w){
12823         tag = tag.toLowerCase();
12824         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12825             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12826                 if(tag == 'input'){
12827                     return w + 2;
12828                 }
12829                 if(tag == 'textarea'){
12830                     return w-2;
12831                 }
12832             }else if(Roo.isOpera){
12833                 if(tag == 'input'){
12834                     return w + 2;
12835                 }
12836                 if(tag == 'textarea'){
12837                     return w-2;
12838                 }
12839             }
12840         }
12841         return w;
12842     },
12843     
12844     setFieldLabel : function(v)
12845     {
12846         if(!this.rendered){
12847             return;
12848         }
12849         
12850         if(this.indicatorEl()){
12851             var ar = this.el.select('label > span',true);
12852             
12853             if (ar.elements.length) {
12854                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12855                 this.fieldLabel = v;
12856                 return;
12857             }
12858             
12859             var br = this.el.select('label',true);
12860             
12861             if(br.elements.length) {
12862                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12863                 this.fieldLabel = v;
12864                 return;
12865             }
12866             
12867             Roo.log('Cannot Found any of label > span || label in input');
12868             return;
12869         }
12870         
12871         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12872         this.fieldLabel = v;
12873         
12874         
12875     }
12876 });
12877
12878  
12879 /*
12880  * - LGPL
12881  *
12882  * Input
12883  * 
12884  */
12885
12886 /**
12887  * @class Roo.bootstrap.TextArea
12888  * @extends Roo.bootstrap.Input
12889  * Bootstrap TextArea class
12890  * @cfg {Number} cols Specifies the visible width of a text area
12891  * @cfg {Number} rows Specifies the visible number of lines in a text area
12892  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12893  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12894  * @cfg {string} html text
12895  * 
12896  * @constructor
12897  * Create a new TextArea
12898  * @param {Object} config The config object
12899  */
12900
12901 Roo.bootstrap.TextArea = function(config){
12902     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12903    
12904 };
12905
12906 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12907      
12908     cols : false,
12909     rows : 5,
12910     readOnly : false,
12911     warp : 'soft',
12912     resize : false,
12913     value: false,
12914     html: false,
12915     
12916     getAutoCreate : function(){
12917         
12918         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12919         
12920         var id = Roo.id();
12921         
12922         var cfg = {};
12923         
12924         if(this.inputType != 'hidden'){
12925             cfg.cls = 'form-group' //input-group
12926         }
12927         
12928         var input =  {
12929             tag: 'textarea',
12930             id : id,
12931             warp : this.warp,
12932             rows : this.rows,
12933             value : this.value || '',
12934             html: this.html || '',
12935             cls : 'form-control',
12936             placeholder : this.placeholder || '' 
12937             
12938         };
12939         
12940         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12941             input.maxLength = this.maxLength;
12942         }
12943         
12944         if(this.resize){
12945             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12946         }
12947         
12948         if(this.cols){
12949             input.cols = this.cols;
12950         }
12951         
12952         if (this.readOnly) {
12953             input.readonly = true;
12954         }
12955         
12956         if (this.name) {
12957             input.name = this.name;
12958         }
12959         
12960         if (this.size) {
12961             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12962         }
12963         
12964         var settings=this;
12965         ['xs','sm','md','lg'].map(function(size){
12966             if (settings[size]) {
12967                 cfg.cls += ' col-' + size + '-' + settings[size];
12968             }
12969         });
12970         
12971         var inputblock = input;
12972         
12973         if(this.hasFeedback && !this.allowBlank){
12974             
12975             var feedback = {
12976                 tag: 'span',
12977                 cls: 'glyphicon form-control-feedback'
12978             };
12979
12980             inputblock = {
12981                 cls : 'has-feedback',
12982                 cn :  [
12983                     input,
12984                     feedback
12985                 ] 
12986             };  
12987         }
12988         
12989         
12990         if (this.before || this.after) {
12991             
12992             inputblock = {
12993                 cls : 'input-group',
12994                 cn :  [] 
12995             };
12996             if (this.before) {
12997                 inputblock.cn.push({
12998                     tag :'span',
12999                     cls : 'input-group-addon',
13000                     html : this.before
13001                 });
13002             }
13003             
13004             inputblock.cn.push(input);
13005             
13006             if(this.hasFeedback && !this.allowBlank){
13007                 inputblock.cls += ' has-feedback';
13008                 inputblock.cn.push(feedback);
13009             }
13010             
13011             if (this.after) {
13012                 inputblock.cn.push({
13013                     tag :'span',
13014                     cls : 'input-group-addon',
13015                     html : this.after
13016                 });
13017             }
13018             
13019         }
13020         
13021         if (align ==='left' && this.fieldLabel.length) {
13022             cfg.cn = [
13023                 {
13024                     tag: 'label',
13025                     'for' :  id,
13026                     cls : 'control-label',
13027                     html : this.fieldLabel
13028                 },
13029                 {
13030                     cls : "",
13031                     cn: [
13032                         inputblock
13033                     ]
13034                 }
13035
13036             ];
13037             
13038             if(this.labelWidth > 12){
13039                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13040             }
13041
13042             if(this.labelWidth < 13 && this.labelmd == 0){
13043                 this.labelmd = this.labelWidth;
13044             }
13045
13046             if(this.labellg > 0){
13047                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13048                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13049             }
13050
13051             if(this.labelmd > 0){
13052                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13053                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13054             }
13055
13056             if(this.labelsm > 0){
13057                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13058                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13059             }
13060
13061             if(this.labelxs > 0){
13062                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13063                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13064             }
13065             
13066         } else if ( this.fieldLabel.length) {
13067             cfg.cn = [
13068
13069                {
13070                    tag: 'label',
13071                    //cls : 'input-group-addon',
13072                    html : this.fieldLabel
13073
13074                },
13075
13076                inputblock
13077
13078            ];
13079
13080         } else {
13081
13082             cfg.cn = [
13083
13084                 inputblock
13085
13086             ];
13087                 
13088         }
13089         
13090         if (this.disabled) {
13091             input.disabled=true;
13092         }
13093         
13094         return cfg;
13095         
13096     },
13097     /**
13098      * return the real textarea element.
13099      */
13100     inputEl: function ()
13101     {
13102         return this.el.select('textarea.form-control',true).first();
13103     },
13104     
13105     /**
13106      * Clear any invalid styles/messages for this field
13107      */
13108     clearInvalid : function()
13109     {
13110         
13111         if(!this.el || this.preventMark){ // not rendered
13112             return;
13113         }
13114         
13115         var label = this.el.select('label', true).first();
13116         var icon = this.el.select('i.fa-star', true).first();
13117         
13118         if(label && icon){
13119             icon.remove();
13120         }
13121         this.el.removeClass( this.validClass);
13122         this.inputEl().removeClass('is-invalid');
13123          
13124         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13125             
13126             var feedback = this.el.select('.form-control-feedback', true).first();
13127             
13128             if(feedback){
13129                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13130             }
13131             
13132         }
13133         
13134         this.fireEvent('valid', this);
13135     },
13136     
13137      /**
13138      * Mark this field as valid
13139      */
13140     markValid : function()
13141     {
13142         if(!this.el  || this.preventMark){ // not rendered
13143             return;
13144         }
13145         
13146         this.el.removeClass([this.invalidClass, this.validClass]);
13147         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13148         
13149         var feedback = this.el.select('.form-control-feedback', true).first();
13150             
13151         if(feedback){
13152             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13153         }
13154
13155         if(this.disabled || this.allowBlank){
13156             return;
13157         }
13158         
13159         var label = this.el.select('label', true).first();
13160         var icon = this.el.select('i.fa-star', true).first();
13161         
13162         if(label && icon){
13163             icon.remove();
13164         }
13165         if (Roo.bootstrap.version == 3) {
13166             this.el.addClass(this.validClass);
13167         } else {
13168             this.inputEl().addClass('is-valid');
13169         }
13170         
13171         
13172         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13173             
13174             var feedback = this.el.select('.form-control-feedback', true).first();
13175             
13176             if(feedback){
13177                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13178                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13179             }
13180             
13181         }
13182         
13183         this.fireEvent('valid', this);
13184     },
13185     
13186      /**
13187      * Mark this field as invalid
13188      * @param {String} msg The validation message
13189      */
13190     markInvalid : function(msg)
13191     {
13192         if(!this.el  || this.preventMark){ // not rendered
13193             return;
13194         }
13195         
13196         this.el.removeClass([this.invalidClass, this.validClass]);
13197         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13198         
13199         var feedback = this.el.select('.form-control-feedback', true).first();
13200             
13201         if(feedback){
13202             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13203         }
13204
13205         if(this.disabled || this.allowBlank){
13206             return;
13207         }
13208         
13209         var label = this.el.select('label', true).first();
13210         var icon = this.el.select('i.fa-star', true).first();
13211         
13212         if(!this.getValue().length && label && !icon){
13213             this.el.createChild({
13214                 tag : 'i',
13215                 cls : 'text-danger fa fa-lg fa-star',
13216                 tooltip : 'This field is required',
13217                 style : 'margin-right:5px;'
13218             }, label, true);
13219         }
13220         
13221         if (Roo.bootstrap.version == 3) {
13222             this.el.addClass(this.invalidClass);
13223         } else {
13224             this.inputEl().addClass('is-invalid');
13225         }
13226         
13227         // fixme ... this may be depricated need to test..
13228         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13229             
13230             var feedback = this.el.select('.form-control-feedback', true).first();
13231             
13232             if(feedback){
13233                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13234                 
13235                 if(this.getValue().length || this.forceFeedback){
13236                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13237                 }
13238                 
13239             }
13240             
13241         }
13242         
13243         this.fireEvent('invalid', this, msg);
13244     }
13245 });
13246
13247  
13248 /*
13249  * - LGPL
13250  *
13251  * trigger field - base class for combo..
13252  * 
13253  */
13254  
13255 /**
13256  * @class Roo.bootstrap.TriggerField
13257  * @extends Roo.bootstrap.Input
13258  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13259  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13260  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13261  * for which you can provide a custom implementation.  For example:
13262  * <pre><code>
13263 var trigger = new Roo.bootstrap.TriggerField();
13264 trigger.onTriggerClick = myTriggerFn;
13265 trigger.applyTo('my-field');
13266 </code></pre>
13267  *
13268  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13269  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13270  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13271  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13272  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13273
13274  * @constructor
13275  * Create a new TriggerField.
13276  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13277  * to the base TextField)
13278  */
13279 Roo.bootstrap.TriggerField = function(config){
13280     this.mimicing = false;
13281     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13282 };
13283
13284 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13285     /**
13286      * @cfg {String} triggerClass A CSS class to apply to the trigger
13287      */
13288      /**
13289      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13290      */
13291     hideTrigger:false,
13292
13293     /**
13294      * @cfg {Boolean} removable (true|false) special filter default false
13295      */
13296     removable : false,
13297     
13298     /** @cfg {Boolean} grow @hide */
13299     /** @cfg {Number} growMin @hide */
13300     /** @cfg {Number} growMax @hide */
13301
13302     /**
13303      * @hide 
13304      * @method
13305      */
13306     autoSize: Roo.emptyFn,
13307     // private
13308     monitorTab : true,
13309     // private
13310     deferHeight : true,
13311
13312     
13313     actionMode : 'wrap',
13314     
13315     caret : false,
13316     
13317     
13318     getAutoCreate : function(){
13319        
13320         var align = this.labelAlign || this.parentLabelAlign();
13321         
13322         var id = Roo.id();
13323         
13324         var cfg = {
13325             cls: 'form-group' //input-group
13326         };
13327         
13328         
13329         var input =  {
13330             tag: 'input',
13331             id : id,
13332             type : this.inputType,
13333             cls : 'form-control',
13334             autocomplete: 'new-password',
13335             placeholder : this.placeholder || '' 
13336             
13337         };
13338         if (this.name) {
13339             input.name = this.name;
13340         }
13341         if (this.size) {
13342             input.cls += ' input-' + this.size;
13343         }
13344         
13345         if (this.disabled) {
13346             input.disabled=true;
13347         }
13348         
13349         var inputblock = input;
13350         
13351         if(this.hasFeedback && !this.allowBlank){
13352             
13353             var feedback = {
13354                 tag: 'span',
13355                 cls: 'glyphicon form-control-feedback'
13356             };
13357             
13358             if(this.removable && !this.editable  ){
13359                 inputblock = {
13360                     cls : 'has-feedback',
13361                     cn :  [
13362                         inputblock,
13363                         {
13364                             tag: 'button',
13365                             html : 'x',
13366                             cls : 'roo-combo-removable-btn close'
13367                         },
13368                         feedback
13369                     ] 
13370                 };
13371             } else {
13372                 inputblock = {
13373                     cls : 'has-feedback',
13374                     cn :  [
13375                         inputblock,
13376                         feedback
13377                     ] 
13378                 };
13379             }
13380
13381         } else {
13382             if(this.removable && !this.editable ){
13383                 inputblock = {
13384                     cls : 'roo-removable',
13385                     cn :  [
13386                         inputblock,
13387                         {
13388                             tag: 'button',
13389                             html : 'x',
13390                             cls : 'roo-combo-removable-btn close'
13391                         }
13392                     ] 
13393                 };
13394             }
13395         }
13396         
13397         if (this.before || this.after) {
13398             
13399             inputblock = {
13400                 cls : 'input-group',
13401                 cn :  [] 
13402             };
13403             if (this.before) {
13404                 inputblock.cn.push({
13405                     tag :'span',
13406                     cls : 'input-group-addon input-group-prepend input-group-text',
13407                     html : this.before
13408                 });
13409             }
13410             
13411             inputblock.cn.push(input);
13412             
13413             if(this.hasFeedback && !this.allowBlank){
13414                 inputblock.cls += ' has-feedback';
13415                 inputblock.cn.push(feedback);
13416             }
13417             
13418             if (this.after) {
13419                 inputblock.cn.push({
13420                     tag :'span',
13421                     cls : 'input-group-addon input-group-append input-group-text',
13422                     html : this.after
13423                 });
13424             }
13425             
13426         };
13427         
13428       
13429         
13430         var ibwrap = inputblock;
13431         
13432         if(this.multiple){
13433             ibwrap = {
13434                 tag: 'ul',
13435                 cls: 'roo-select2-choices',
13436                 cn:[
13437                     {
13438                         tag: 'li',
13439                         cls: 'roo-select2-search-field',
13440                         cn: [
13441
13442                             inputblock
13443                         ]
13444                     }
13445                 ]
13446             };
13447                 
13448         }
13449         
13450         var combobox = {
13451             cls: 'roo-select2-container input-group',
13452             cn: [
13453                  {
13454                     tag: 'input',
13455                     type : 'hidden',
13456                     cls: 'form-hidden-field'
13457                 },
13458                 ibwrap
13459             ]
13460         };
13461         
13462         if(!this.multiple && this.showToggleBtn){
13463             
13464             var caret = {
13465                         tag: 'span',
13466                         cls: 'caret'
13467              };
13468             if (this.caret != false) {
13469                 caret = {
13470                      tag: 'i',
13471                      cls: 'fa fa-' + this.caret
13472                 };
13473                 
13474             }
13475             
13476             combobox.cn.push({
13477                 tag :'span',
13478                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13479                 cn : [
13480                     Roo.bootstrap.version == 3 ? caret : '',
13481                     {
13482                         tag: 'span',
13483                         cls: 'combobox-clear',
13484                         cn  : [
13485                             {
13486                                 tag : 'i',
13487                                 cls: 'icon-remove'
13488                             }
13489                         ]
13490                     }
13491                 ]
13492
13493             })
13494         }
13495         
13496         if(this.multiple){
13497             combobox.cls += ' roo-select2-container-multi';
13498         }
13499          var indicator = {
13500             tag : 'i',
13501             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13502             tooltip : 'This field is required'
13503         };
13504         if (Roo.bootstrap.version == 4) {
13505             indicator = {
13506                 tag : 'i',
13507                 style : 'display:none'
13508             };
13509         }
13510         
13511         
13512         if (align ==='left' && this.fieldLabel.length) {
13513             
13514             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13515
13516             cfg.cn = [
13517                 indicator,
13518                 {
13519                     tag: 'label',
13520                     'for' :  id,
13521                     cls : 'control-label',
13522                     html : this.fieldLabel
13523
13524                 },
13525                 {
13526                     cls : "", 
13527                     cn: [
13528                         combobox
13529                     ]
13530                 }
13531
13532             ];
13533             
13534             var labelCfg = cfg.cn[1];
13535             var contentCfg = cfg.cn[2];
13536             
13537             if(this.indicatorpos == 'right'){
13538                 cfg.cn = [
13539                     {
13540                         tag: 'label',
13541                         'for' :  id,
13542                         cls : 'control-label',
13543                         cn : [
13544                             {
13545                                 tag : 'span',
13546                                 html : this.fieldLabel
13547                             },
13548                             indicator
13549                         ]
13550                     },
13551                     {
13552                         cls : "", 
13553                         cn: [
13554                             combobox
13555                         ]
13556                     }
13557
13558                 ];
13559                 
13560                 labelCfg = cfg.cn[0];
13561                 contentCfg = cfg.cn[1];
13562             }
13563             
13564             if(this.labelWidth > 12){
13565                 labelCfg.style = "width: " + this.labelWidth + 'px';
13566             }
13567             
13568             if(this.labelWidth < 13 && this.labelmd == 0){
13569                 this.labelmd = this.labelWidth;
13570             }
13571             
13572             if(this.labellg > 0){
13573                 labelCfg.cls += ' col-lg-' + this.labellg;
13574                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13575             }
13576             
13577             if(this.labelmd > 0){
13578                 labelCfg.cls += ' col-md-' + this.labelmd;
13579                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13580             }
13581             
13582             if(this.labelsm > 0){
13583                 labelCfg.cls += ' col-sm-' + this.labelsm;
13584                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13585             }
13586             
13587             if(this.labelxs > 0){
13588                 labelCfg.cls += ' col-xs-' + this.labelxs;
13589                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13590             }
13591             
13592         } else if ( this.fieldLabel.length) {
13593 //                Roo.log(" label");
13594             cfg.cn = [
13595                 indicator,
13596                {
13597                    tag: 'label',
13598                    //cls : 'input-group-addon',
13599                    html : this.fieldLabel
13600
13601                },
13602
13603                combobox
13604
13605             ];
13606             
13607             if(this.indicatorpos == 'right'){
13608                 
13609                 cfg.cn = [
13610                     {
13611                        tag: 'label',
13612                        cn : [
13613                            {
13614                                tag : 'span',
13615                                html : this.fieldLabel
13616                            },
13617                            indicator
13618                        ]
13619
13620                     },
13621                     combobox
13622
13623                 ];
13624
13625             }
13626
13627         } else {
13628             
13629 //                Roo.log(" no label && no align");
13630                 cfg = combobox
13631                      
13632                 
13633         }
13634         
13635         var settings=this;
13636         ['xs','sm','md','lg'].map(function(size){
13637             if (settings[size]) {
13638                 cfg.cls += ' col-' + size + '-' + settings[size];
13639             }
13640         });
13641         
13642         return cfg;
13643         
13644     },
13645     
13646     
13647     
13648     // private
13649     onResize : function(w, h){
13650 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13651 //        if(typeof w == 'number'){
13652 //            var x = w - this.trigger.getWidth();
13653 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13654 //            this.trigger.setStyle('left', x+'px');
13655 //        }
13656     },
13657
13658     // private
13659     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13660
13661     // private
13662     getResizeEl : function(){
13663         return this.inputEl();
13664     },
13665
13666     // private
13667     getPositionEl : function(){
13668         return this.inputEl();
13669     },
13670
13671     // private
13672     alignErrorIcon : function(){
13673         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13674     },
13675
13676     // private
13677     initEvents : function(){
13678         
13679         this.createList();
13680         
13681         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13682         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13683         if(!this.multiple && this.showToggleBtn){
13684             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13685             if(this.hideTrigger){
13686                 this.trigger.setDisplayed(false);
13687             }
13688             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13689         }
13690         
13691         if(this.multiple){
13692             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13693         }
13694         
13695         if(this.removable && !this.editable && !this.tickable){
13696             var close = this.closeTriggerEl();
13697             
13698             if(close){
13699                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13700                 close.on('click', this.removeBtnClick, this, close);
13701             }
13702         }
13703         
13704         //this.trigger.addClassOnOver('x-form-trigger-over');
13705         //this.trigger.addClassOnClick('x-form-trigger-click');
13706         
13707         //if(!this.width){
13708         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13709         //}
13710     },
13711     
13712     closeTriggerEl : function()
13713     {
13714         var close = this.el.select('.roo-combo-removable-btn', true).first();
13715         return close ? close : false;
13716     },
13717     
13718     removeBtnClick : function(e, h, el)
13719     {
13720         e.preventDefault();
13721         
13722         if(this.fireEvent("remove", this) !== false){
13723             this.reset();
13724             this.fireEvent("afterremove", this)
13725         }
13726     },
13727     
13728     createList : function()
13729     {
13730         this.list = Roo.get(document.body).createChild({
13731             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13732             cls: 'typeahead typeahead-long dropdown-menu shadow',
13733             style: 'display:none'
13734         });
13735         
13736         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13737         
13738     },
13739
13740     // private
13741     initTrigger : function(){
13742        
13743     },
13744
13745     // private
13746     onDestroy : function(){
13747         if(this.trigger){
13748             this.trigger.removeAllListeners();
13749           //  this.trigger.remove();
13750         }
13751         //if(this.wrap){
13752         //    this.wrap.remove();
13753         //}
13754         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13755     },
13756
13757     // private
13758     onFocus : function(){
13759         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13760         /*
13761         if(!this.mimicing){
13762             this.wrap.addClass('x-trigger-wrap-focus');
13763             this.mimicing = true;
13764             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13765             if(this.monitorTab){
13766                 this.el.on("keydown", this.checkTab, this);
13767             }
13768         }
13769         */
13770     },
13771
13772     // private
13773     checkTab : function(e){
13774         if(e.getKey() == e.TAB){
13775             this.triggerBlur();
13776         }
13777     },
13778
13779     // private
13780     onBlur : function(){
13781         // do nothing
13782     },
13783
13784     // private
13785     mimicBlur : function(e, t){
13786         /*
13787         if(!this.wrap.contains(t) && this.validateBlur()){
13788             this.triggerBlur();
13789         }
13790         */
13791     },
13792
13793     // private
13794     triggerBlur : function(){
13795         this.mimicing = false;
13796         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13797         if(this.monitorTab){
13798             this.el.un("keydown", this.checkTab, this);
13799         }
13800         //this.wrap.removeClass('x-trigger-wrap-focus');
13801         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13802     },
13803
13804     // private
13805     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13806     validateBlur : function(e, t){
13807         return true;
13808     },
13809
13810     // private
13811     onDisable : function(){
13812         this.inputEl().dom.disabled = true;
13813         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13814         //if(this.wrap){
13815         //    this.wrap.addClass('x-item-disabled');
13816         //}
13817     },
13818
13819     // private
13820     onEnable : function(){
13821         this.inputEl().dom.disabled = false;
13822         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13823         //if(this.wrap){
13824         //    this.el.removeClass('x-item-disabled');
13825         //}
13826     },
13827
13828     // private
13829     onShow : function(){
13830         var ae = this.getActionEl();
13831         
13832         if(ae){
13833             ae.dom.style.display = '';
13834             ae.dom.style.visibility = 'visible';
13835         }
13836     },
13837
13838     // private
13839     
13840     onHide : function(){
13841         var ae = this.getActionEl();
13842         ae.dom.style.display = 'none';
13843     },
13844
13845     /**
13846      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13847      * by an implementing function.
13848      * @method
13849      * @param {EventObject} e
13850      */
13851     onTriggerClick : Roo.emptyFn
13852 });
13853  
13854 /*
13855 * Licence: LGPL
13856 */
13857
13858 /**
13859  * @class Roo.bootstrap.CardUploader
13860  * @extends Roo.bootstrap.Button
13861  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13862  * @cfg {Number} errorTimeout default 3000
13863  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13864  * @cfg {Array}  html The button text.
13865
13866  *
13867  * @constructor
13868  * Create a new CardUploader
13869  * @param {Object} config The config object
13870  */
13871
13872 Roo.bootstrap.CardUploader = function(config){
13873     
13874  
13875     
13876     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13877     
13878     
13879     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13880         return r.data.id
13881      });
13882     
13883      this.addEvents({
13884          // raw events
13885         /**
13886          * @event preview
13887          * When a image is clicked on - and needs to display a slideshow or similar..
13888          * @param {Roo.bootstrap.Card} this
13889          * @param {Object} The image information data 
13890          *
13891          */
13892         'preview' : true,
13893          /**
13894          * @event download
13895          * When a the download link is clicked
13896          * @param {Roo.bootstrap.Card} this
13897          * @param {Object} The image information data  contains 
13898          */
13899         'download' : true
13900         
13901     });
13902 };
13903  
13904 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13905     
13906      
13907     errorTimeout : 3000,
13908      
13909     images : false,
13910    
13911     fileCollection : false,
13912     allowBlank : true,
13913     
13914     getAutoCreate : function()
13915     {
13916         
13917         var cfg =  {
13918             cls :'form-group' ,
13919             cn : [
13920                
13921                 {
13922                     tag: 'label',
13923                    //cls : 'input-group-addon',
13924                     html : this.fieldLabel
13925
13926                 },
13927
13928                 {
13929                     tag: 'input',
13930                     type : 'hidden',
13931                     name : this.name,
13932                     value : this.value,
13933                     cls : 'd-none  form-control'
13934                 },
13935                 
13936                 {
13937                     tag: 'input',
13938                     multiple : 'multiple',
13939                     type : 'file',
13940                     cls : 'd-none  roo-card-upload-selector'
13941                 },
13942                 
13943                 {
13944                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13945                 },
13946                 {
13947                     cls : 'card-columns roo-card-uploader-container'
13948                 }
13949
13950             ]
13951         };
13952            
13953          
13954         return cfg;
13955     },
13956     
13957     getChildContainer : function() /// what children are added to.
13958     {
13959         return this.containerEl;
13960     },
13961    
13962     getButtonContainer : function() /// what children are added to.
13963     {
13964         return this.el.select(".roo-card-uploader-button-container").first();
13965     },
13966    
13967     initEvents : function()
13968     {
13969         
13970         Roo.bootstrap.Input.prototype.initEvents.call(this);
13971         
13972         var t = this;
13973         this.addxtype({
13974             xns: Roo.bootstrap,
13975
13976             xtype : 'Button',
13977             container_method : 'getButtonContainer' ,            
13978             html :  this.html, // fix changable?
13979             cls : 'w-100 ',
13980             listeners : {
13981                 'click' : function(btn, e) {
13982                     t.onClick(e);
13983                 }
13984             }
13985         });
13986         
13987         
13988         
13989         
13990         this.urlAPI = (window.createObjectURL && window) || 
13991                                 (window.URL && URL.revokeObjectURL && URL) || 
13992                                 (window.webkitURL && webkitURL);
13993                         
13994          
13995          
13996          
13997         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13998         
13999         this.selectorEl.on('change', this.onFileSelected, this);
14000         if (this.images) {
14001             var t = this;
14002             this.images.forEach(function(img) {
14003                 t.addCard(img)
14004             });
14005             this.images = false;
14006         }
14007         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14008          
14009        
14010     },
14011     
14012    
14013     onClick : function(e)
14014     {
14015         e.preventDefault();
14016          
14017         this.selectorEl.dom.click();
14018          
14019     },
14020     
14021     onFileSelected : function(e)
14022     {
14023         e.preventDefault();
14024         
14025         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14026             return;
14027         }
14028         
14029         Roo.each(this.selectorEl.dom.files, function(file){    
14030             this.addFile(file);
14031         }, this);
14032          
14033     },
14034     
14035       
14036     
14037       
14038     
14039     addFile : function(file)
14040     {
14041            
14042         if(typeof(file) === 'string'){
14043             throw "Add file by name?"; // should not happen
14044             return;
14045         }
14046         
14047         if(!file || !this.urlAPI){
14048             return;
14049         }
14050         
14051         // file;
14052         // file.type;
14053         
14054         var _this = this;
14055         
14056         
14057         var url = _this.urlAPI.createObjectURL( file);
14058            
14059         this.addCard({
14060             id : Roo.bootstrap.CardUploader.ID--,
14061             is_uploaded : false,
14062             src : url,
14063             srcfile : file,
14064             title : file.name,
14065             mimetype : file.type,
14066             preview : false,
14067             is_deleted : 0
14068         });
14069         
14070     },
14071     
14072     /**
14073      * addCard - add an Attachment to the uploader
14074      * @param data - the data about the image to upload
14075      *
14076      * {
14077           id : 123
14078           title : "Title of file",
14079           is_uploaded : false,
14080           src : "http://.....",
14081           srcfile : { the File upload object },
14082           mimetype : file.type,
14083           preview : false,
14084           is_deleted : 0
14085           .. any other data...
14086         }
14087      *
14088      * 
14089     */
14090     
14091     addCard : function (data)
14092     {
14093         // hidden input element?
14094         // if the file is not an image...
14095         //then we need to use something other that and header_image
14096         var t = this;
14097         //   remove.....
14098         var footer = [
14099             {
14100                 xns : Roo.bootstrap,
14101                 xtype : 'CardFooter',
14102                  items: [
14103                     {
14104                         xns : Roo.bootstrap,
14105                         xtype : 'Element',
14106                         cls : 'd-flex',
14107                         items : [
14108                             
14109                             {
14110                                 xns : Roo.bootstrap,
14111                                 xtype : 'Button',
14112                                 html : String.format("<small>{0}</small>", data.title),
14113                                 cls : 'col-10 text-left',
14114                                 size: 'sm',
14115                                 weight: 'link',
14116                                 fa : 'download',
14117                                 listeners : {
14118                                     click : function() {
14119                                      
14120                                         t.fireEvent( "download", t, data );
14121                                     }
14122                                 }
14123                             },
14124                           
14125                             {
14126                                 xns : Roo.bootstrap,
14127                                 xtype : 'Button',
14128                                 style: 'max-height: 28px; ',
14129                                 size : 'sm',
14130                                 weight: 'danger',
14131                                 cls : 'col-2',
14132                                 fa : 'times',
14133                                 listeners : {
14134                                     click : function() {
14135                                         t.removeCard(data.id)
14136                                     }
14137                                 }
14138                             }
14139                         ]
14140                     }
14141                     
14142                 ] 
14143             }
14144             
14145         ];
14146         
14147         var cn = this.addxtype(
14148             {
14149                  
14150                 xns : Roo.bootstrap,
14151                 xtype : 'Card',
14152                 closeable : true,
14153                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14154                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14155                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14156                 data : data,
14157                 html : false,
14158                  
14159                 items : footer,
14160                 initEvents : function() {
14161                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14162                     var card = this;
14163                     this.imgEl = this.el.select('.card-img-top').first();
14164                     if (this.imgEl) {
14165                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14166                         this.imgEl.set({ 'pointer' : 'cursor' });
14167                                   
14168                     }
14169                     this.getCardFooter().addClass('p-1');
14170                     
14171                   
14172                 }
14173                 
14174             }
14175         );
14176         // dont' really need ot update items.
14177         // this.items.push(cn);
14178         this.fileCollection.add(cn);
14179         
14180         if (!data.srcfile) {
14181             this.updateInput();
14182             return;
14183         }
14184             
14185         var _t = this;
14186         var reader = new FileReader();
14187         reader.addEventListener("load", function() {  
14188             data.srcdata =  reader.result;
14189             _t.updateInput();
14190         });
14191         reader.readAsDataURL(data.srcfile);
14192         
14193         
14194         
14195     },
14196     removeCard : function(id)
14197     {
14198         
14199         var card  = this.fileCollection.get(id);
14200         card.data.is_deleted = 1;
14201         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14202         //this.fileCollection.remove(card);
14203         //this.items = this.items.filter(function(e) { return e != card });
14204         // dont' really need ot update items.
14205         card.el.dom.parentNode.removeChild(card.el.dom);
14206         this.updateInput();
14207
14208         
14209     },
14210     reset: function()
14211     {
14212         this.fileCollection.each(function(card) {
14213             if (card.el.dom && card.el.dom.parentNode) {
14214                 card.el.dom.parentNode.removeChild(card.el.dom);
14215             }
14216         });
14217         this.fileCollection.clear();
14218         this.updateInput();
14219     },
14220     
14221     updateInput : function()
14222     {
14223          var data = [];
14224         this.fileCollection.each(function(e) {
14225             data.push(e.data);
14226             
14227         });
14228         this.inputEl().dom.value = JSON.stringify(data);
14229         
14230         
14231         
14232     }
14233     
14234     
14235 });
14236
14237
14238 Roo.bootstrap.CardUploader.ID = -1;/*
14239  * Based on:
14240  * Ext JS Library 1.1.1
14241  * Copyright(c) 2006-2007, Ext JS, LLC.
14242  *
14243  * Originally Released Under LGPL - original licence link has changed is not relivant.
14244  *
14245  * Fork - LGPL
14246  * <script type="text/javascript">
14247  */
14248
14249
14250 /**
14251  * @class Roo.data.SortTypes
14252  * @singleton
14253  * Defines the default sorting (casting?) comparison functions used when sorting data.
14254  */
14255 Roo.data.SortTypes = {
14256     /**
14257      * Default sort that does nothing
14258      * @param {Mixed} s The value being converted
14259      * @return {Mixed} The comparison value
14260      */
14261     none : function(s){
14262         return s;
14263     },
14264     
14265     /**
14266      * The regular expression used to strip tags
14267      * @type {RegExp}
14268      * @property
14269      */
14270     stripTagsRE : /<\/?[^>]+>/gi,
14271     
14272     /**
14273      * Strips all HTML tags to sort on text only
14274      * @param {Mixed} s The value being converted
14275      * @return {String} The comparison value
14276      */
14277     asText : function(s){
14278         return String(s).replace(this.stripTagsRE, "");
14279     },
14280     
14281     /**
14282      * Strips all HTML tags to sort on text only - Case insensitive
14283      * @param {Mixed} s The value being converted
14284      * @return {String} The comparison value
14285      */
14286     asUCText : function(s){
14287         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14288     },
14289     
14290     /**
14291      * Case insensitive string
14292      * @param {Mixed} s The value being converted
14293      * @return {String} The comparison value
14294      */
14295     asUCString : function(s) {
14296         return String(s).toUpperCase();
14297     },
14298     
14299     /**
14300      * Date sorting
14301      * @param {Mixed} s The value being converted
14302      * @return {Number} The comparison value
14303      */
14304     asDate : function(s) {
14305         if(!s){
14306             return 0;
14307         }
14308         if(s instanceof Date){
14309             return s.getTime();
14310         }
14311         return Date.parse(String(s));
14312     },
14313     
14314     /**
14315      * Float sorting
14316      * @param {Mixed} s The value being converted
14317      * @return {Float} The comparison value
14318      */
14319     asFloat : function(s) {
14320         var val = parseFloat(String(s).replace(/,/g, ""));
14321         if(isNaN(val)) {
14322             val = 0;
14323         }
14324         return val;
14325     },
14326     
14327     /**
14328      * Integer sorting
14329      * @param {Mixed} s The value being converted
14330      * @return {Number} The comparison value
14331      */
14332     asInt : function(s) {
14333         var val = parseInt(String(s).replace(/,/g, ""));
14334         if(isNaN(val)) {
14335             val = 0;
14336         }
14337         return val;
14338     }
14339 };/*
14340  * Based on:
14341  * Ext JS Library 1.1.1
14342  * Copyright(c) 2006-2007, Ext JS, LLC.
14343  *
14344  * Originally Released Under LGPL - original licence link has changed is not relivant.
14345  *
14346  * Fork - LGPL
14347  * <script type="text/javascript">
14348  */
14349
14350 /**
14351 * @class Roo.data.Record
14352  * Instances of this class encapsulate both record <em>definition</em> information, and record
14353  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14354  * to access Records cached in an {@link Roo.data.Store} object.<br>
14355  * <p>
14356  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14357  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14358  * objects.<br>
14359  * <p>
14360  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14361  * @constructor
14362  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14363  * {@link #create}. The parameters are the same.
14364  * @param {Array} data An associative Array of data values keyed by the field name.
14365  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14366  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14367  * not specified an integer id is generated.
14368  */
14369 Roo.data.Record = function(data, id){
14370     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14371     this.data = data;
14372 };
14373
14374 /**
14375  * Generate a constructor for a specific record layout.
14376  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14377  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14378  * Each field definition object may contain the following properties: <ul>
14379  * <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,
14380  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14381  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14382  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14383  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14384  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14385  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14386  * this may be omitted.</p></li>
14387  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14388  * <ul><li>auto (Default, implies no conversion)</li>
14389  * <li>string</li>
14390  * <li>int</li>
14391  * <li>float</li>
14392  * <li>boolean</li>
14393  * <li>date</li></ul></p></li>
14394  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14395  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14396  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14397  * by the Reader into an object that will be stored in the Record. It is passed the
14398  * following parameters:<ul>
14399  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14400  * </ul></p></li>
14401  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14402  * </ul>
14403  * <br>usage:<br><pre><code>
14404 var TopicRecord = Roo.data.Record.create(
14405     {name: 'title', mapping: 'topic_title'},
14406     {name: 'author', mapping: 'username'},
14407     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14408     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14409     {name: 'lastPoster', mapping: 'user2'},
14410     {name: 'excerpt', mapping: 'post_text'}
14411 );
14412
14413 var myNewRecord = new TopicRecord({
14414     title: 'Do my job please',
14415     author: 'noobie',
14416     totalPosts: 1,
14417     lastPost: new Date(),
14418     lastPoster: 'Animal',
14419     excerpt: 'No way dude!'
14420 });
14421 myStore.add(myNewRecord);
14422 </code></pre>
14423  * @method create
14424  * @static
14425  */
14426 Roo.data.Record.create = function(o){
14427     var f = function(){
14428         f.superclass.constructor.apply(this, arguments);
14429     };
14430     Roo.extend(f, Roo.data.Record);
14431     var p = f.prototype;
14432     p.fields = new Roo.util.MixedCollection(false, function(field){
14433         return field.name;
14434     });
14435     for(var i = 0, len = o.length; i < len; i++){
14436         p.fields.add(new Roo.data.Field(o[i]));
14437     }
14438     f.getField = function(name){
14439         return p.fields.get(name);  
14440     };
14441     return f;
14442 };
14443
14444 Roo.data.Record.AUTO_ID = 1000;
14445 Roo.data.Record.EDIT = 'edit';
14446 Roo.data.Record.REJECT = 'reject';
14447 Roo.data.Record.COMMIT = 'commit';
14448
14449 Roo.data.Record.prototype = {
14450     /**
14451      * Readonly flag - true if this record has been modified.
14452      * @type Boolean
14453      */
14454     dirty : false,
14455     editing : false,
14456     error: null,
14457     modified: null,
14458
14459     // private
14460     join : function(store){
14461         this.store = store;
14462     },
14463
14464     /**
14465      * Set the named field to the specified value.
14466      * @param {String} name The name of the field to set.
14467      * @param {Object} value The value to set the field to.
14468      */
14469     set : function(name, value){
14470         if(this.data[name] == value){
14471             return;
14472         }
14473         this.dirty = true;
14474         if(!this.modified){
14475             this.modified = {};
14476         }
14477         if(typeof this.modified[name] == 'undefined'){
14478             this.modified[name] = this.data[name];
14479         }
14480         this.data[name] = value;
14481         if(!this.editing && this.store){
14482             this.store.afterEdit(this);
14483         }       
14484     },
14485
14486     /**
14487      * Get the value of the named field.
14488      * @param {String} name The name of the field to get the value of.
14489      * @return {Object} The value of the field.
14490      */
14491     get : function(name){
14492         return this.data[name]; 
14493     },
14494
14495     // private
14496     beginEdit : function(){
14497         this.editing = true;
14498         this.modified = {}; 
14499     },
14500
14501     // private
14502     cancelEdit : function(){
14503         this.editing = false;
14504         delete this.modified;
14505     },
14506
14507     // private
14508     endEdit : function(){
14509         this.editing = false;
14510         if(this.dirty && this.store){
14511             this.store.afterEdit(this);
14512         }
14513     },
14514
14515     /**
14516      * Usually called by the {@link Roo.data.Store} which owns the Record.
14517      * Rejects all changes made to the Record since either creation, or the last commit operation.
14518      * Modified fields are reverted to their original values.
14519      * <p>
14520      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14521      * of reject operations.
14522      */
14523     reject : function(){
14524         var m = this.modified;
14525         for(var n in m){
14526             if(typeof m[n] != "function"){
14527                 this.data[n] = m[n];
14528             }
14529         }
14530         this.dirty = false;
14531         delete this.modified;
14532         this.editing = false;
14533         if(this.store){
14534             this.store.afterReject(this);
14535         }
14536     },
14537
14538     /**
14539      * Usually called by the {@link Roo.data.Store} which owns the Record.
14540      * Commits all changes made to the Record since either creation, or the last commit operation.
14541      * <p>
14542      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14543      * of commit operations.
14544      */
14545     commit : function(){
14546         this.dirty = false;
14547         delete this.modified;
14548         this.editing = false;
14549         if(this.store){
14550             this.store.afterCommit(this);
14551         }
14552     },
14553
14554     // private
14555     hasError : function(){
14556         return this.error != null;
14557     },
14558
14559     // private
14560     clearError : function(){
14561         this.error = null;
14562     },
14563
14564     /**
14565      * Creates a copy of this record.
14566      * @param {String} id (optional) A new record id if you don't want to use this record's id
14567      * @return {Record}
14568      */
14569     copy : function(newId) {
14570         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14571     }
14572 };/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583
14584
14585 /**
14586  * @class Roo.data.Store
14587  * @extends Roo.util.Observable
14588  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14589  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14590  * <p>
14591  * 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
14592  * has no knowledge of the format of the data returned by the Proxy.<br>
14593  * <p>
14594  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14595  * instances from the data object. These records are cached and made available through accessor functions.
14596  * @constructor
14597  * Creates a new Store.
14598  * @param {Object} config A config object containing the objects needed for the Store to access data,
14599  * and read the data into Records.
14600  */
14601 Roo.data.Store = function(config){
14602     this.data = new Roo.util.MixedCollection(false);
14603     this.data.getKey = function(o){
14604         return o.id;
14605     };
14606     this.baseParams = {};
14607     // private
14608     this.paramNames = {
14609         "start" : "start",
14610         "limit" : "limit",
14611         "sort" : "sort",
14612         "dir" : "dir",
14613         "multisort" : "_multisort"
14614     };
14615
14616     if(config && config.data){
14617         this.inlineData = config.data;
14618         delete config.data;
14619     }
14620
14621     Roo.apply(this, config);
14622     
14623     if(this.reader){ // reader passed
14624         this.reader = Roo.factory(this.reader, Roo.data);
14625         this.reader.xmodule = this.xmodule || false;
14626         if(!this.recordType){
14627             this.recordType = this.reader.recordType;
14628         }
14629         if(this.reader.onMetaChange){
14630             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14631         }
14632     }
14633
14634     if(this.recordType){
14635         this.fields = this.recordType.prototype.fields;
14636     }
14637     this.modified = [];
14638
14639     this.addEvents({
14640         /**
14641          * @event datachanged
14642          * Fires when the data cache has changed, and a widget which is using this Store
14643          * as a Record cache should refresh its view.
14644          * @param {Store} this
14645          */
14646         datachanged : true,
14647         /**
14648          * @event metachange
14649          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14650          * @param {Store} this
14651          * @param {Object} meta The JSON metadata
14652          */
14653         metachange : true,
14654         /**
14655          * @event add
14656          * Fires when Records have been added to the Store
14657          * @param {Store} this
14658          * @param {Roo.data.Record[]} records The array of Records added
14659          * @param {Number} index The index at which the record(s) were added
14660          */
14661         add : true,
14662         /**
14663          * @event remove
14664          * Fires when a Record has been removed from the Store
14665          * @param {Store} this
14666          * @param {Roo.data.Record} record The Record that was removed
14667          * @param {Number} index The index at which the record was removed
14668          */
14669         remove : true,
14670         /**
14671          * @event update
14672          * Fires when a Record has been updated
14673          * @param {Store} this
14674          * @param {Roo.data.Record} record The Record that was updated
14675          * @param {String} operation The update operation being performed.  Value may be one of:
14676          * <pre><code>
14677  Roo.data.Record.EDIT
14678  Roo.data.Record.REJECT
14679  Roo.data.Record.COMMIT
14680          * </code></pre>
14681          */
14682         update : true,
14683         /**
14684          * @event clear
14685          * Fires when the data cache has been cleared.
14686          * @param {Store} this
14687          */
14688         clear : true,
14689         /**
14690          * @event beforeload
14691          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14692          * the load action will be canceled.
14693          * @param {Store} this
14694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14695          */
14696         beforeload : true,
14697         /**
14698          * @event beforeloadadd
14699          * Fires after a new set of Records has been loaded.
14700          * @param {Store} this
14701          * @param {Roo.data.Record[]} records The Records that were loaded
14702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14703          */
14704         beforeloadadd : true,
14705         /**
14706          * @event load
14707          * Fires after a new set of Records has been loaded, before they are added to the store.
14708          * @param {Store} this
14709          * @param {Roo.data.Record[]} records The Records that were loaded
14710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14711          * @params {Object} return from reader
14712          */
14713         load : true,
14714         /**
14715          * @event loadexception
14716          * Fires if an exception occurs in the Proxy during loading.
14717          * Called with the signature of the Proxy's "loadexception" event.
14718          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14719          * 
14720          * @param {Proxy} 
14721          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14722          * @param {Object} load options 
14723          * @param {Object} jsonData from your request (normally this contains the Exception)
14724          */
14725         loadexception : true
14726     });
14727     
14728     if(this.proxy){
14729         this.proxy = Roo.factory(this.proxy, Roo.data);
14730         this.proxy.xmodule = this.xmodule || false;
14731         this.relayEvents(this.proxy,  ["loadexception"]);
14732     }
14733     this.sortToggle = {};
14734     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14735
14736     Roo.data.Store.superclass.constructor.call(this);
14737
14738     if(this.inlineData){
14739         this.loadData(this.inlineData);
14740         delete this.inlineData;
14741     }
14742 };
14743
14744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14745      /**
14746     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14747     * without a remote query - used by combo/forms at present.
14748     */
14749     
14750     /**
14751     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14752     */
14753     /**
14754     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14755     */
14756     /**
14757     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14758     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14759     */
14760     /**
14761     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14762     * on any HTTP request
14763     */
14764     /**
14765     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14766     */
14767     /**
14768     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14769     */
14770     multiSort: false,
14771     /**
14772     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14773     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14774     */
14775     remoteSort : false,
14776
14777     /**
14778     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14779      * loaded or when a record is removed. (defaults to false).
14780     */
14781     pruneModifiedRecords : false,
14782
14783     // private
14784     lastOptions : null,
14785
14786     /**
14787      * Add Records to the Store and fires the add event.
14788      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14789      */
14790     add : function(records){
14791         records = [].concat(records);
14792         for(var i = 0, len = records.length; i < len; i++){
14793             records[i].join(this);
14794         }
14795         var index = this.data.length;
14796         this.data.addAll(records);
14797         this.fireEvent("add", this, records, index);
14798     },
14799
14800     /**
14801      * Remove a Record from the Store and fires the remove event.
14802      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14803      */
14804     remove : function(record){
14805         var index = this.data.indexOf(record);
14806         this.data.removeAt(index);
14807  
14808         if(this.pruneModifiedRecords){
14809             this.modified.remove(record);
14810         }
14811         this.fireEvent("remove", this, record, index);
14812     },
14813
14814     /**
14815      * Remove all Records from the Store and fires the clear event.
14816      */
14817     removeAll : function(){
14818         this.data.clear();
14819         if(this.pruneModifiedRecords){
14820             this.modified = [];
14821         }
14822         this.fireEvent("clear", this);
14823     },
14824
14825     /**
14826      * Inserts Records to the Store at the given index and fires the add event.
14827      * @param {Number} index The start index at which to insert the passed Records.
14828      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14829      */
14830     insert : function(index, records){
14831         records = [].concat(records);
14832         for(var i = 0, len = records.length; i < len; i++){
14833             this.data.insert(index, records[i]);
14834             records[i].join(this);
14835         }
14836         this.fireEvent("add", this, records, index);
14837     },
14838
14839     /**
14840      * Get the index within the cache of the passed Record.
14841      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14842      * @return {Number} The index of the passed Record. Returns -1 if not found.
14843      */
14844     indexOf : function(record){
14845         return this.data.indexOf(record);
14846     },
14847
14848     /**
14849      * Get the index within the cache of the Record with the passed id.
14850      * @param {String} id The id of the Record to find.
14851      * @return {Number} The index of the Record. Returns -1 if not found.
14852      */
14853     indexOfId : function(id){
14854         return this.data.indexOfKey(id);
14855     },
14856
14857     /**
14858      * Get the Record with the specified id.
14859      * @param {String} id The id of the Record to find.
14860      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14861      */
14862     getById : function(id){
14863         return this.data.key(id);
14864     },
14865
14866     /**
14867      * Get the Record at the specified index.
14868      * @param {Number} index The index of the Record to find.
14869      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14870      */
14871     getAt : function(index){
14872         return this.data.itemAt(index);
14873     },
14874
14875     /**
14876      * Returns a range of Records between specified indices.
14877      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14878      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14879      * @return {Roo.data.Record[]} An array of Records
14880      */
14881     getRange : function(start, end){
14882         return this.data.getRange(start, end);
14883     },
14884
14885     // private
14886     storeOptions : function(o){
14887         o = Roo.apply({}, o);
14888         delete o.callback;
14889         delete o.scope;
14890         this.lastOptions = o;
14891     },
14892
14893     /**
14894      * Loads the Record cache from the configured Proxy using the configured Reader.
14895      * <p>
14896      * If using remote paging, then the first load call must specify the <em>start</em>
14897      * and <em>limit</em> properties in the options.params property to establish the initial
14898      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14899      * <p>
14900      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14901      * and this call will return before the new data has been loaded. Perform any post-processing
14902      * in a callback function, or in a "load" event handler.</strong>
14903      * <p>
14904      * @param {Object} options An object containing properties which control loading options:<ul>
14905      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14906      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14907      * passed the following arguments:<ul>
14908      * <li>r : Roo.data.Record[]</li>
14909      * <li>options: Options object from the load call</li>
14910      * <li>success: Boolean success indicator</li></ul></li>
14911      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14912      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14913      * </ul>
14914      */
14915     load : function(options){
14916         options = options || {};
14917         if(this.fireEvent("beforeload", this, options) !== false){
14918             this.storeOptions(options);
14919             var p = Roo.apply(options.params || {}, this.baseParams);
14920             // if meta was not loaded from remote source.. try requesting it.
14921             if (!this.reader.metaFromRemote) {
14922                 p._requestMeta = 1;
14923             }
14924             if(this.sortInfo && this.remoteSort){
14925                 var pn = this.paramNames;
14926                 p[pn["sort"]] = this.sortInfo.field;
14927                 p[pn["dir"]] = this.sortInfo.direction;
14928             }
14929             if (this.multiSort) {
14930                 var pn = this.paramNames;
14931                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14932             }
14933             
14934             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14935         }
14936     },
14937
14938     /**
14939      * Reloads the Record cache from the configured Proxy using the configured Reader and
14940      * the options from the last load operation performed.
14941      * @param {Object} options (optional) An object containing properties which may override the options
14942      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14943      * the most recently used options are reused).
14944      */
14945     reload : function(options){
14946         this.load(Roo.applyIf(options||{}, this.lastOptions));
14947     },
14948
14949     // private
14950     // Called as a callback by the Reader during a load operation.
14951     loadRecords : function(o, options, success){
14952         if(!o || success === false){
14953             if(success !== false){
14954                 this.fireEvent("load", this, [], options, o);
14955             }
14956             if(options.callback){
14957                 options.callback.call(options.scope || this, [], options, false);
14958             }
14959             return;
14960         }
14961         // if data returned failure - throw an exception.
14962         if (o.success === false) {
14963             // show a message if no listener is registered.
14964             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14965                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14966             }
14967             // loadmask wil be hooked into this..
14968             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14969             return;
14970         }
14971         var r = o.records, t = o.totalRecords || r.length;
14972         
14973         this.fireEvent("beforeloadadd", this, r, options, o);
14974         
14975         if(!options || options.add !== true){
14976             if(this.pruneModifiedRecords){
14977                 this.modified = [];
14978             }
14979             for(var i = 0, len = r.length; i < len; i++){
14980                 r[i].join(this);
14981             }
14982             if(this.snapshot){
14983                 this.data = this.snapshot;
14984                 delete this.snapshot;
14985             }
14986             this.data.clear();
14987             this.data.addAll(r);
14988             this.totalLength = t;
14989             this.applySort();
14990             this.fireEvent("datachanged", this);
14991         }else{
14992             this.totalLength = Math.max(t, this.data.length+r.length);
14993             this.add(r);
14994         }
14995         
14996         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14997                 
14998             var e = new Roo.data.Record({});
14999
15000             e.set(this.parent.displayField, this.parent.emptyTitle);
15001             e.set(this.parent.valueField, '');
15002
15003             this.insert(0, e);
15004         }
15005             
15006         this.fireEvent("load", this, r, options, o);
15007         if(options.callback){
15008             options.callback.call(options.scope || this, r, options, true);
15009         }
15010     },
15011
15012
15013     /**
15014      * Loads data from a passed data block. A Reader which understands the format of the data
15015      * must have been configured in the constructor.
15016      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15017      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15018      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15019      */
15020     loadData : function(o, append){
15021         var r = this.reader.readRecords(o);
15022         this.loadRecords(r, {add: append}, true);
15023     },
15024     
15025      /**
15026      * using 'cn' the nested child reader read the child array into it's child stores.
15027      * @param {Object} rec The record with a 'children array
15028      */
15029     loadDataFromChildren : function(rec)
15030     {
15031         this.loadData(this.reader.toLoadData(rec));
15032     },
15033     
15034
15035     /**
15036      * Gets the number of cached records.
15037      * <p>
15038      * <em>If using paging, this may not be the total size of the dataset. If the data object
15039      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15040      * the data set size</em>
15041      */
15042     getCount : function(){
15043         return this.data.length || 0;
15044     },
15045
15046     /**
15047      * Gets the total number of records in the dataset as returned by the server.
15048      * <p>
15049      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15050      * the dataset size</em>
15051      */
15052     getTotalCount : function(){
15053         return this.totalLength || 0;
15054     },
15055
15056     /**
15057      * Returns the sort state of the Store as an object with two properties:
15058      * <pre><code>
15059  field {String} The name of the field by which the Records are sorted
15060  direction {String} The sort order, "ASC" or "DESC"
15061      * </code></pre>
15062      */
15063     getSortState : function(){
15064         return this.sortInfo;
15065     },
15066
15067     // private
15068     applySort : function(){
15069         if(this.sortInfo && !this.remoteSort){
15070             var s = this.sortInfo, f = s.field;
15071             var st = this.fields.get(f).sortType;
15072             var fn = function(r1, r2){
15073                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15074                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15075             };
15076             this.data.sort(s.direction, fn);
15077             if(this.snapshot && this.snapshot != this.data){
15078                 this.snapshot.sort(s.direction, fn);
15079             }
15080         }
15081     },
15082
15083     /**
15084      * Sets the default sort column and order to be used by the next load operation.
15085      * @param {String} fieldName The name of the field to sort by.
15086      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15087      */
15088     setDefaultSort : function(field, dir){
15089         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15090     },
15091
15092     /**
15093      * Sort the Records.
15094      * If remote sorting is used, the sort is performed on the server, and the cache is
15095      * reloaded. If local sorting is used, the cache is sorted internally.
15096      * @param {String} fieldName The name of the field to sort by.
15097      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15098      */
15099     sort : function(fieldName, dir){
15100         var f = this.fields.get(fieldName);
15101         if(!dir){
15102             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15103             
15104             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15105                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15106             }else{
15107                 dir = f.sortDir;
15108             }
15109         }
15110         this.sortToggle[f.name] = dir;
15111         this.sortInfo = {field: f.name, direction: dir};
15112         if(!this.remoteSort){
15113             this.applySort();
15114             this.fireEvent("datachanged", this);
15115         }else{
15116             this.load(this.lastOptions);
15117         }
15118     },
15119
15120     /**
15121      * Calls the specified function for each of the Records in the cache.
15122      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15123      * Returning <em>false</em> aborts and exits the iteration.
15124      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15125      */
15126     each : function(fn, scope){
15127         this.data.each(fn, scope);
15128     },
15129
15130     /**
15131      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15132      * (e.g., during paging).
15133      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15134      */
15135     getModifiedRecords : function(){
15136         return this.modified;
15137     },
15138
15139     // private
15140     createFilterFn : function(property, value, anyMatch){
15141         if(!value.exec){ // not a regex
15142             value = String(value);
15143             if(value.length == 0){
15144                 return false;
15145             }
15146             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15147         }
15148         return function(r){
15149             return value.test(r.data[property]);
15150         };
15151     },
15152
15153     /**
15154      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15155      * @param {String} property A field on your records
15156      * @param {Number} start The record index to start at (defaults to 0)
15157      * @param {Number} end The last record index to include (defaults to length - 1)
15158      * @return {Number} The sum
15159      */
15160     sum : function(property, start, end){
15161         var rs = this.data.items, v = 0;
15162         start = start || 0;
15163         end = (end || end === 0) ? end : rs.length-1;
15164
15165         for(var i = start; i <= end; i++){
15166             v += (rs[i].data[property] || 0);
15167         }
15168         return v;
15169     },
15170
15171     /**
15172      * Filter the records by a specified property.
15173      * @param {String} field A field on your records
15174      * @param {String/RegExp} value Either a string that the field
15175      * should start with or a RegExp to test against the field
15176      * @param {Boolean} anyMatch True to match any part not just the beginning
15177      */
15178     filter : function(property, value, anyMatch){
15179         var fn = this.createFilterFn(property, value, anyMatch);
15180         return fn ? this.filterBy(fn) : this.clearFilter();
15181     },
15182
15183     /**
15184      * Filter by a function. The specified function will be called with each
15185      * record in this data source. If the function returns true the record is included,
15186      * otherwise it is filtered.
15187      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15188      * @param {Object} scope (optional) The scope of the function (defaults to this)
15189      */
15190     filterBy : function(fn, scope){
15191         this.snapshot = this.snapshot || this.data;
15192         this.data = this.queryBy(fn, scope||this);
15193         this.fireEvent("datachanged", this);
15194     },
15195
15196     /**
15197      * Query the records by a specified property.
15198      * @param {String} field A field on your records
15199      * @param {String/RegExp} value Either a string that the field
15200      * should start with or a RegExp to test against the field
15201      * @param {Boolean} anyMatch True to match any part not just the beginning
15202      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15203      */
15204     query : function(property, value, anyMatch){
15205         var fn = this.createFilterFn(property, value, anyMatch);
15206         return fn ? this.queryBy(fn) : this.data.clone();
15207     },
15208
15209     /**
15210      * Query by a function. The specified function will be called with each
15211      * record in this data source. If the function returns true the record is included
15212      * in the results.
15213      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15214      * @param {Object} scope (optional) The scope of the function (defaults to this)
15215       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15216      **/
15217     queryBy : function(fn, scope){
15218         var data = this.snapshot || this.data;
15219         return data.filterBy(fn, scope||this);
15220     },
15221
15222     /**
15223      * Collects unique values for a particular dataIndex from this store.
15224      * @param {String} dataIndex The property to collect
15225      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15226      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15227      * @return {Array} An array of the unique values
15228      **/
15229     collect : function(dataIndex, allowNull, bypassFilter){
15230         var d = (bypassFilter === true && this.snapshot) ?
15231                 this.snapshot.items : this.data.items;
15232         var v, sv, r = [], l = {};
15233         for(var i = 0, len = d.length; i < len; i++){
15234             v = d[i].data[dataIndex];
15235             sv = String(v);
15236             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15237                 l[sv] = true;
15238                 r[r.length] = v;
15239             }
15240         }
15241         return r;
15242     },
15243
15244     /**
15245      * Revert to a view of the Record cache with no filtering applied.
15246      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15247      */
15248     clearFilter : function(suppressEvent){
15249         if(this.snapshot && this.snapshot != this.data){
15250             this.data = this.snapshot;
15251             delete this.snapshot;
15252             if(suppressEvent !== true){
15253                 this.fireEvent("datachanged", this);
15254             }
15255         }
15256     },
15257
15258     // private
15259     afterEdit : function(record){
15260         if(this.modified.indexOf(record) == -1){
15261             this.modified.push(record);
15262         }
15263         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15264     },
15265     
15266     // private
15267     afterReject : function(record){
15268         this.modified.remove(record);
15269         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15270     },
15271
15272     // private
15273     afterCommit : function(record){
15274         this.modified.remove(record);
15275         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15276     },
15277
15278     /**
15279      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15280      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15281      */
15282     commitChanges : function(){
15283         var m = this.modified.slice(0);
15284         this.modified = [];
15285         for(var i = 0, len = m.length; i < len; i++){
15286             m[i].commit();
15287         }
15288     },
15289
15290     /**
15291      * Cancel outstanding changes on all changed records.
15292      */
15293     rejectChanges : function(){
15294         var m = this.modified.slice(0);
15295         this.modified = [];
15296         for(var i = 0, len = m.length; i < len; i++){
15297             m[i].reject();
15298         }
15299     },
15300
15301     onMetaChange : function(meta, rtype, o){
15302         this.recordType = rtype;
15303         this.fields = rtype.prototype.fields;
15304         delete this.snapshot;
15305         this.sortInfo = meta.sortInfo || this.sortInfo;
15306         this.modified = [];
15307         this.fireEvent('metachange', this, this.reader.meta);
15308     },
15309     
15310     moveIndex : function(data, type)
15311     {
15312         var index = this.indexOf(data);
15313         
15314         var newIndex = index + type;
15315         
15316         this.remove(data);
15317         
15318         this.insert(newIndex, data);
15319         
15320     }
15321 });/*
15322  * Based on:
15323  * Ext JS Library 1.1.1
15324  * Copyright(c) 2006-2007, Ext JS, LLC.
15325  *
15326  * Originally Released Under LGPL - original licence link has changed is not relivant.
15327  *
15328  * Fork - LGPL
15329  * <script type="text/javascript">
15330  */
15331
15332 /**
15333  * @class Roo.data.SimpleStore
15334  * @extends Roo.data.Store
15335  * Small helper class to make creating Stores from Array data easier.
15336  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15337  * @cfg {Array} fields An array of field definition objects, or field name strings.
15338  * @cfg {Object} an existing reader (eg. copied from another store)
15339  * @cfg {Array} data The multi-dimensional array of data
15340  * @constructor
15341  * @param {Object} config
15342  */
15343 Roo.data.SimpleStore = function(config)
15344 {
15345     Roo.data.SimpleStore.superclass.constructor.call(this, {
15346         isLocal : true,
15347         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15348                 id: config.id
15349             },
15350             Roo.data.Record.create(config.fields)
15351         ),
15352         proxy : new Roo.data.MemoryProxy(config.data)
15353     });
15354     this.load();
15355 };
15356 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15357  * Based on:
15358  * Ext JS Library 1.1.1
15359  * Copyright(c) 2006-2007, Ext JS, LLC.
15360  *
15361  * Originally Released Under LGPL - original licence link has changed is not relivant.
15362  *
15363  * Fork - LGPL
15364  * <script type="text/javascript">
15365  */
15366
15367 /**
15368 /**
15369  * @extends Roo.data.Store
15370  * @class Roo.data.JsonStore
15371  * Small helper class to make creating Stores for JSON data easier. <br/>
15372 <pre><code>
15373 var store = new Roo.data.JsonStore({
15374     url: 'get-images.php',
15375     root: 'images',
15376     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15377 });
15378 </code></pre>
15379  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15380  * JsonReader and HttpProxy (unless inline data is provided).</b>
15381  * @cfg {Array} fields An array of field definition objects, or field name strings.
15382  * @constructor
15383  * @param {Object} config
15384  */
15385 Roo.data.JsonStore = function(c){
15386     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15387         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15388         reader: new Roo.data.JsonReader(c, c.fields)
15389     }));
15390 };
15391 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15392  * Based on:
15393  * Ext JS Library 1.1.1
15394  * Copyright(c) 2006-2007, Ext JS, LLC.
15395  *
15396  * Originally Released Under LGPL - original licence link has changed is not relivant.
15397  *
15398  * Fork - LGPL
15399  * <script type="text/javascript">
15400  */
15401
15402  
15403 Roo.data.Field = function(config){
15404     if(typeof config == "string"){
15405         config = {name: config};
15406     }
15407     Roo.apply(this, config);
15408     
15409     if(!this.type){
15410         this.type = "auto";
15411     }
15412     
15413     var st = Roo.data.SortTypes;
15414     // named sortTypes are supported, here we look them up
15415     if(typeof this.sortType == "string"){
15416         this.sortType = st[this.sortType];
15417     }
15418     
15419     // set default sortType for strings and dates
15420     if(!this.sortType){
15421         switch(this.type){
15422             case "string":
15423                 this.sortType = st.asUCString;
15424                 break;
15425             case "date":
15426                 this.sortType = st.asDate;
15427                 break;
15428             default:
15429                 this.sortType = st.none;
15430         }
15431     }
15432
15433     // define once
15434     var stripRe = /[\$,%]/g;
15435
15436     // prebuilt conversion function for this field, instead of
15437     // switching every time we're reading a value
15438     if(!this.convert){
15439         var cv, dateFormat = this.dateFormat;
15440         switch(this.type){
15441             case "":
15442             case "auto":
15443             case undefined:
15444                 cv = function(v){ return v; };
15445                 break;
15446             case "string":
15447                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15448                 break;
15449             case "int":
15450                 cv = function(v){
15451                     return v !== undefined && v !== null && v !== '' ?
15452                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15453                     };
15454                 break;
15455             case "float":
15456                 cv = function(v){
15457                     return v !== undefined && v !== null && v !== '' ?
15458                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15459                     };
15460                 break;
15461             case "bool":
15462             case "boolean":
15463                 cv = function(v){ return v === true || v === "true" || v == 1; };
15464                 break;
15465             case "date":
15466                 cv = function(v){
15467                     if(!v){
15468                         return '';
15469                     }
15470                     if(v instanceof Date){
15471                         return v;
15472                     }
15473                     if(dateFormat){
15474                         if(dateFormat == "timestamp"){
15475                             return new Date(v*1000);
15476                         }
15477                         return Date.parseDate(v, dateFormat);
15478                     }
15479                     var parsed = Date.parse(v);
15480                     return parsed ? new Date(parsed) : null;
15481                 };
15482              break;
15483             
15484         }
15485         this.convert = cv;
15486     }
15487 };
15488
15489 Roo.data.Field.prototype = {
15490     dateFormat: null,
15491     defaultValue: "",
15492     mapping: null,
15493     sortType : null,
15494     sortDir : "ASC"
15495 };/*
15496  * Based on:
15497  * Ext JS Library 1.1.1
15498  * Copyright(c) 2006-2007, Ext JS, LLC.
15499  *
15500  * Originally Released Under LGPL - original licence link has changed is not relivant.
15501  *
15502  * Fork - LGPL
15503  * <script type="text/javascript">
15504  */
15505  
15506 // Base class for reading structured data from a data source.  This class is intended to be
15507 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15508
15509 /**
15510  * @class Roo.data.DataReader
15511  * Base class for reading structured data from a data source.  This class is intended to be
15512  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15513  */
15514
15515 Roo.data.DataReader = function(meta, recordType){
15516     
15517     this.meta = meta;
15518     
15519     this.recordType = recordType instanceof Array ? 
15520         Roo.data.Record.create(recordType) : recordType;
15521 };
15522
15523 Roo.data.DataReader.prototype = {
15524     
15525     
15526     readerType : 'Data',
15527      /**
15528      * Create an empty record
15529      * @param {Object} data (optional) - overlay some values
15530      * @return {Roo.data.Record} record created.
15531      */
15532     newRow :  function(d) {
15533         var da =  {};
15534         this.recordType.prototype.fields.each(function(c) {
15535             switch( c.type) {
15536                 case 'int' : da[c.name] = 0; break;
15537                 case 'date' : da[c.name] = new Date(); break;
15538                 case 'float' : da[c.name] = 0.0; break;
15539                 case 'boolean' : da[c.name] = false; break;
15540                 default : da[c.name] = ""; break;
15541             }
15542             
15543         });
15544         return new this.recordType(Roo.apply(da, d));
15545     }
15546     
15547     
15548 };/*
15549  * Based on:
15550  * Ext JS Library 1.1.1
15551  * Copyright(c) 2006-2007, Ext JS, LLC.
15552  *
15553  * Originally Released Under LGPL - original licence link has changed is not relivant.
15554  *
15555  * Fork - LGPL
15556  * <script type="text/javascript">
15557  */
15558
15559 /**
15560  * @class Roo.data.DataProxy
15561  * @extends Roo.data.Observable
15562  * This class is an abstract base class for implementations which provide retrieval of
15563  * unformatted data objects.<br>
15564  * <p>
15565  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15566  * (of the appropriate type which knows how to parse the data object) to provide a block of
15567  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15568  * <p>
15569  * Custom implementations must implement the load method as described in
15570  * {@link Roo.data.HttpProxy#load}.
15571  */
15572 Roo.data.DataProxy = function(){
15573     this.addEvents({
15574         /**
15575          * @event beforeload
15576          * Fires before a network request is made to retrieve a data object.
15577          * @param {Object} This DataProxy object.
15578          * @param {Object} params The params parameter to the load function.
15579          */
15580         beforeload : true,
15581         /**
15582          * @event load
15583          * Fires before the load method's callback is called.
15584          * @param {Object} This DataProxy object.
15585          * @param {Object} o The data object.
15586          * @param {Object} arg The callback argument object passed to the load function.
15587          */
15588         load : true,
15589         /**
15590          * @event loadexception
15591          * Fires if an Exception occurs during data retrieval.
15592          * @param {Object} This DataProxy object.
15593          * @param {Object} o The data object.
15594          * @param {Object} arg The callback argument object passed to the load function.
15595          * @param {Object} e The Exception.
15596          */
15597         loadexception : true
15598     });
15599     Roo.data.DataProxy.superclass.constructor.call(this);
15600 };
15601
15602 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15603
15604     /**
15605      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15606      */
15607 /*
15608  * Based on:
15609  * Ext JS Library 1.1.1
15610  * Copyright(c) 2006-2007, Ext JS, LLC.
15611  *
15612  * Originally Released Under LGPL - original licence link has changed is not relivant.
15613  *
15614  * Fork - LGPL
15615  * <script type="text/javascript">
15616  */
15617 /**
15618  * @class Roo.data.MemoryProxy
15619  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15620  * to the Reader when its load method is called.
15621  * @constructor
15622  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15623  */
15624 Roo.data.MemoryProxy = function(data){
15625     if (data.data) {
15626         data = data.data;
15627     }
15628     Roo.data.MemoryProxy.superclass.constructor.call(this);
15629     this.data = data;
15630 };
15631
15632 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15633     
15634     /**
15635      * Load data from the requested source (in this case an in-memory
15636      * data object passed to the constructor), read the data object into
15637      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15638      * process that block using the passed callback.
15639      * @param {Object} params This parameter is not used by the MemoryProxy class.
15640      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15641      * object into a block of Roo.data.Records.
15642      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15643      * The function must be passed <ul>
15644      * <li>The Record block object</li>
15645      * <li>The "arg" argument from the load function</li>
15646      * <li>A boolean success indicator</li>
15647      * </ul>
15648      * @param {Object} scope The scope in which to call the callback
15649      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15650      */
15651     load : function(params, reader, callback, scope, arg){
15652         params = params || {};
15653         var result;
15654         try {
15655             result = reader.readRecords(params.data ? params.data :this.data);
15656         }catch(e){
15657             this.fireEvent("loadexception", this, arg, null, e);
15658             callback.call(scope, null, arg, false);
15659             return;
15660         }
15661         callback.call(scope, result, arg, true);
15662     },
15663     
15664     // private
15665     update : function(params, records){
15666         
15667     }
15668 });/*
15669  * Based on:
15670  * Ext JS Library 1.1.1
15671  * Copyright(c) 2006-2007, Ext JS, LLC.
15672  *
15673  * Originally Released Under LGPL - original licence link has changed is not relivant.
15674  *
15675  * Fork - LGPL
15676  * <script type="text/javascript">
15677  */
15678 /**
15679  * @class Roo.data.HttpProxy
15680  * @extends Roo.data.DataProxy
15681  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15682  * configured to reference a certain URL.<br><br>
15683  * <p>
15684  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15685  * from which the running page was served.<br><br>
15686  * <p>
15687  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15688  * <p>
15689  * Be aware that to enable the browser to parse an XML document, the server must set
15690  * the Content-Type header in the HTTP response to "text/xml".
15691  * @constructor
15692  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15693  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15694  * will be used to make the request.
15695  */
15696 Roo.data.HttpProxy = function(conn){
15697     Roo.data.HttpProxy.superclass.constructor.call(this);
15698     // is conn a conn config or a real conn?
15699     this.conn = conn;
15700     this.useAjax = !conn || !conn.events;
15701   
15702 };
15703
15704 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15705     // thse are take from connection...
15706     
15707     /**
15708      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15709      */
15710     /**
15711      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15712      * extra parameters to each request made by this object. (defaults to undefined)
15713      */
15714     /**
15715      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15716      *  to each request made by this object. (defaults to undefined)
15717      */
15718     /**
15719      * @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)
15720      */
15721     /**
15722      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15723      */
15724      /**
15725      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15726      * @type Boolean
15727      */
15728   
15729
15730     /**
15731      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15732      * @type Boolean
15733      */
15734     /**
15735      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15736      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15737      * a finer-grained basis than the DataProxy events.
15738      */
15739     getConnection : function(){
15740         return this.useAjax ? Roo.Ajax : this.conn;
15741     },
15742
15743     /**
15744      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15745      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15746      * process that block using the passed callback.
15747      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15748      * for the request to the remote server.
15749      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15750      * object into a block of Roo.data.Records.
15751      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15752      * The function must be passed <ul>
15753      * <li>The Record block object</li>
15754      * <li>The "arg" argument from the load function</li>
15755      * <li>A boolean success indicator</li>
15756      * </ul>
15757      * @param {Object} scope The scope in which to call the callback
15758      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15759      */
15760     load : function(params, reader, callback, scope, arg){
15761         if(this.fireEvent("beforeload", this, params) !== false){
15762             var  o = {
15763                 params : params || {},
15764                 request: {
15765                     callback : callback,
15766                     scope : scope,
15767                     arg : arg
15768                 },
15769                 reader: reader,
15770                 callback : this.loadResponse,
15771                 scope: this
15772             };
15773             if(this.useAjax){
15774                 Roo.applyIf(o, this.conn);
15775                 if(this.activeRequest){
15776                     Roo.Ajax.abort(this.activeRequest);
15777                 }
15778                 this.activeRequest = Roo.Ajax.request(o);
15779             }else{
15780                 this.conn.request(o);
15781             }
15782         }else{
15783             callback.call(scope||this, null, arg, false);
15784         }
15785     },
15786
15787     // private
15788     loadResponse : function(o, success, response){
15789         delete this.activeRequest;
15790         if(!success){
15791             this.fireEvent("loadexception", this, o, response);
15792             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15793             return;
15794         }
15795         var result;
15796         try {
15797             result = o.reader.read(response);
15798         }catch(e){
15799             this.fireEvent("loadexception", this, o, response, e);
15800             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15801             return;
15802         }
15803         
15804         this.fireEvent("load", this, o, o.request.arg);
15805         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15806     },
15807
15808     // private
15809     update : function(dataSet){
15810
15811     },
15812
15813     // private
15814     updateResponse : function(dataSet){
15815
15816     }
15817 });/*
15818  * Based on:
15819  * Ext JS Library 1.1.1
15820  * Copyright(c) 2006-2007, Ext JS, LLC.
15821  *
15822  * Originally Released Under LGPL - original licence link has changed is not relivant.
15823  *
15824  * Fork - LGPL
15825  * <script type="text/javascript">
15826  */
15827
15828 /**
15829  * @class Roo.data.ScriptTagProxy
15830  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15831  * other than the originating domain of the running page.<br><br>
15832  * <p>
15833  * <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
15834  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15835  * <p>
15836  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15837  * source code that is used as the source inside a &lt;script> tag.<br><br>
15838  * <p>
15839  * In order for the browser to process the returned data, the server must wrap the data object
15840  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15841  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15842  * depending on whether the callback name was passed:
15843  * <p>
15844  * <pre><code>
15845 boolean scriptTag = false;
15846 String cb = request.getParameter("callback");
15847 if (cb != null) {
15848     scriptTag = true;
15849     response.setContentType("text/javascript");
15850 } else {
15851     response.setContentType("application/x-json");
15852 }
15853 Writer out = response.getWriter();
15854 if (scriptTag) {
15855     out.write(cb + "(");
15856 }
15857 out.print(dataBlock.toJsonString());
15858 if (scriptTag) {
15859     out.write(");");
15860 }
15861 </pre></code>
15862  *
15863  * @constructor
15864  * @param {Object} config A configuration object.
15865  */
15866 Roo.data.ScriptTagProxy = function(config){
15867     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15868     Roo.apply(this, config);
15869     this.head = document.getElementsByTagName("head")[0];
15870 };
15871
15872 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15873
15874 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15875     /**
15876      * @cfg {String} url The URL from which to request the data object.
15877      */
15878     /**
15879      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15880      */
15881     timeout : 30000,
15882     /**
15883      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15884      * the server the name of the callback function set up by the load call to process the returned data object.
15885      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15886      * javascript output which calls this named function passing the data object as its only parameter.
15887      */
15888     callbackParam : "callback",
15889     /**
15890      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15891      * name to the request.
15892      */
15893     nocache : true,
15894
15895     /**
15896      * Load data from the configured URL, read the data object into
15897      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15898      * process that block using the passed callback.
15899      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15900      * for the request to the remote server.
15901      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15902      * object into a block of Roo.data.Records.
15903      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15904      * The function must be passed <ul>
15905      * <li>The Record block object</li>
15906      * <li>The "arg" argument from the load function</li>
15907      * <li>A boolean success indicator</li>
15908      * </ul>
15909      * @param {Object} scope The scope in which to call the callback
15910      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15911      */
15912     load : function(params, reader, callback, scope, arg){
15913         if(this.fireEvent("beforeload", this, params) !== false){
15914
15915             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15916
15917             var url = this.url;
15918             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15919             if(this.nocache){
15920                 url += "&_dc=" + (new Date().getTime());
15921             }
15922             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15923             var trans = {
15924                 id : transId,
15925                 cb : "stcCallback"+transId,
15926                 scriptId : "stcScript"+transId,
15927                 params : params,
15928                 arg : arg,
15929                 url : url,
15930                 callback : callback,
15931                 scope : scope,
15932                 reader : reader
15933             };
15934             var conn = this;
15935
15936             window[trans.cb] = function(o){
15937                 conn.handleResponse(o, trans);
15938             };
15939
15940             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15941
15942             if(this.autoAbort !== false){
15943                 this.abort();
15944             }
15945
15946             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15947
15948             var script = document.createElement("script");
15949             script.setAttribute("src", url);
15950             script.setAttribute("type", "text/javascript");
15951             script.setAttribute("id", trans.scriptId);
15952             this.head.appendChild(script);
15953
15954             this.trans = trans;
15955         }else{
15956             callback.call(scope||this, null, arg, false);
15957         }
15958     },
15959
15960     // private
15961     isLoading : function(){
15962         return this.trans ? true : false;
15963     },
15964
15965     /**
15966      * Abort the current server request.
15967      */
15968     abort : function(){
15969         if(this.isLoading()){
15970             this.destroyTrans(this.trans);
15971         }
15972     },
15973
15974     // private
15975     destroyTrans : function(trans, isLoaded){
15976         this.head.removeChild(document.getElementById(trans.scriptId));
15977         clearTimeout(trans.timeoutId);
15978         if(isLoaded){
15979             window[trans.cb] = undefined;
15980             try{
15981                 delete window[trans.cb];
15982             }catch(e){}
15983         }else{
15984             // if hasn't been loaded, wait for load to remove it to prevent script error
15985             window[trans.cb] = function(){
15986                 window[trans.cb] = undefined;
15987                 try{
15988                     delete window[trans.cb];
15989                 }catch(e){}
15990             };
15991         }
15992     },
15993
15994     // private
15995     handleResponse : function(o, trans){
15996         this.trans = false;
15997         this.destroyTrans(trans, true);
15998         var result;
15999         try {
16000             result = trans.reader.readRecords(o);
16001         }catch(e){
16002             this.fireEvent("loadexception", this, o, trans.arg, e);
16003             trans.callback.call(trans.scope||window, null, trans.arg, false);
16004             return;
16005         }
16006         this.fireEvent("load", this, o, trans.arg);
16007         trans.callback.call(trans.scope||window, result, trans.arg, true);
16008     },
16009
16010     // private
16011     handleFailure : function(trans){
16012         this.trans = false;
16013         this.destroyTrans(trans, false);
16014         this.fireEvent("loadexception", this, null, trans.arg);
16015         trans.callback.call(trans.scope||window, null, trans.arg, false);
16016     }
16017 });/*
16018  * Based on:
16019  * Ext JS Library 1.1.1
16020  * Copyright(c) 2006-2007, Ext JS, LLC.
16021  *
16022  * Originally Released Under LGPL - original licence link has changed is not relivant.
16023  *
16024  * Fork - LGPL
16025  * <script type="text/javascript">
16026  */
16027
16028 /**
16029  * @class Roo.data.JsonReader
16030  * @extends Roo.data.DataReader
16031  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16032  * based on mappings in a provided Roo.data.Record constructor.
16033  * 
16034  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16035  * in the reply previously. 
16036  * 
16037  * <p>
16038  * Example code:
16039  * <pre><code>
16040 var RecordDef = Roo.data.Record.create([
16041     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16042     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16043 ]);
16044 var myReader = new Roo.data.JsonReader({
16045     totalProperty: "results",    // The property which contains the total dataset size (optional)
16046     root: "rows",                // The property which contains an Array of row objects
16047     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16048 }, RecordDef);
16049 </code></pre>
16050  * <p>
16051  * This would consume a JSON file like this:
16052  * <pre><code>
16053 { 'results': 2, 'rows': [
16054     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16055     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16056 }
16057 </code></pre>
16058  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16059  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16060  * paged from the remote server.
16061  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16062  * @cfg {String} root name of the property which contains the Array of row objects.
16063  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16064  * @cfg {Array} fields Array of field definition objects
16065  * @constructor
16066  * Create a new JsonReader
16067  * @param {Object} meta Metadata configuration options
16068  * @param {Object} recordType Either an Array of field definition objects,
16069  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16070  */
16071 Roo.data.JsonReader = function(meta, recordType){
16072     
16073     meta = meta || {};
16074     // set some defaults:
16075     Roo.applyIf(meta, {
16076         totalProperty: 'total',
16077         successProperty : 'success',
16078         root : 'data',
16079         id : 'id'
16080     });
16081     
16082     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16083 };
16084 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16085     
16086     readerType : 'Json',
16087     
16088     /**
16089      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16090      * Used by Store query builder to append _requestMeta to params.
16091      * 
16092      */
16093     metaFromRemote : false,
16094     /**
16095      * This method is only used by a DataProxy which has retrieved data from a remote server.
16096      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16097      * @return {Object} data A data block which is used by an Roo.data.Store object as
16098      * a cache of Roo.data.Records.
16099      */
16100     read : function(response){
16101         var json = response.responseText;
16102        
16103         var o = /* eval:var:o */ eval("("+json+")");
16104         if(!o) {
16105             throw {message: "JsonReader.read: Json object not found"};
16106         }
16107         
16108         if(o.metaData){
16109             
16110             delete this.ef;
16111             this.metaFromRemote = true;
16112             this.meta = o.metaData;
16113             this.recordType = Roo.data.Record.create(o.metaData.fields);
16114             this.onMetaChange(this.meta, this.recordType, o);
16115         }
16116         return this.readRecords(o);
16117     },
16118
16119     // private function a store will implement
16120     onMetaChange : function(meta, recordType, o){
16121
16122     },
16123
16124     /**
16125          * @ignore
16126          */
16127     simpleAccess: function(obj, subsc) {
16128         return obj[subsc];
16129     },
16130
16131         /**
16132          * @ignore
16133          */
16134     getJsonAccessor: function(){
16135         var re = /[\[\.]/;
16136         return function(expr) {
16137             try {
16138                 return(re.test(expr))
16139                     ? new Function("obj", "return obj." + expr)
16140                     : function(obj){
16141                         return obj[expr];
16142                     };
16143             } catch(e){}
16144             return Roo.emptyFn;
16145         };
16146     }(),
16147
16148     /**
16149      * Create a data block containing Roo.data.Records from an XML document.
16150      * @param {Object} o An object which contains an Array of row objects in the property specified
16151      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16152      * which contains the total size of the dataset.
16153      * @return {Object} data A data block which is used by an Roo.data.Store object as
16154      * a cache of Roo.data.Records.
16155      */
16156     readRecords : function(o){
16157         /**
16158          * After any data loads, the raw JSON data is available for further custom processing.
16159          * @type Object
16160          */
16161         this.o = o;
16162         var s = this.meta, Record = this.recordType,
16163             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16164
16165 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16166         if (!this.ef) {
16167             if(s.totalProperty) {
16168                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16169                 }
16170                 if(s.successProperty) {
16171                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16172                 }
16173                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16174                 if (s.id) {
16175                         var g = this.getJsonAccessor(s.id);
16176                         this.getId = function(rec) {
16177                                 var r = g(rec);  
16178                                 return (r === undefined || r === "") ? null : r;
16179                         };
16180                 } else {
16181                         this.getId = function(){return null;};
16182                 }
16183             this.ef = [];
16184             for(var jj = 0; jj < fl; jj++){
16185                 f = fi[jj];
16186                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16187                 this.ef[jj] = this.getJsonAccessor(map);
16188             }
16189         }
16190
16191         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16192         if(s.totalProperty){
16193             var vt = parseInt(this.getTotal(o), 10);
16194             if(!isNaN(vt)){
16195                 totalRecords = vt;
16196             }
16197         }
16198         if(s.successProperty){
16199             var vs = this.getSuccess(o);
16200             if(vs === false || vs === 'false'){
16201                 success = false;
16202             }
16203         }
16204         var records = [];
16205         for(var i = 0; i < c; i++){
16206                 var n = root[i];
16207             var values = {};
16208             var id = this.getId(n);
16209             for(var j = 0; j < fl; j++){
16210                 f = fi[j];
16211             var v = this.ef[j](n);
16212             if (!f.convert) {
16213                 Roo.log('missing convert for ' + f.name);
16214                 Roo.log(f);
16215                 continue;
16216             }
16217             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16218             }
16219             var record = new Record(values, id);
16220             record.json = n;
16221             records[i] = record;
16222         }
16223         return {
16224             raw : o,
16225             success : success,
16226             records : records,
16227             totalRecords : totalRecords
16228         };
16229     },
16230     // used when loading children.. @see loadDataFromChildren
16231     toLoadData: function(rec)
16232     {
16233         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16234         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16235         return { data : data, total : data.length };
16236         
16237     }
16238 });/*
16239  * Based on:
16240  * Ext JS Library 1.1.1
16241  * Copyright(c) 2006-2007, Ext JS, LLC.
16242  *
16243  * Originally Released Under LGPL - original licence link has changed is not relivant.
16244  *
16245  * Fork - LGPL
16246  * <script type="text/javascript">
16247  */
16248
16249 /**
16250  * @class Roo.data.ArrayReader
16251  * @extends Roo.data.DataReader
16252  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16253  * Each element of that Array represents a row of data fields. The
16254  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16255  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16256  * <p>
16257  * Example code:.
16258  * <pre><code>
16259 var RecordDef = Roo.data.Record.create([
16260     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16261     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16262 ]);
16263 var myReader = new Roo.data.ArrayReader({
16264     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16265 }, RecordDef);
16266 </code></pre>
16267  * <p>
16268  * This would consume an Array like this:
16269  * <pre><code>
16270 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16271   </code></pre>
16272  
16273  * @constructor
16274  * Create a new JsonReader
16275  * @param {Object} meta Metadata configuration options.
16276  * @param {Object|Array} recordType Either an Array of field definition objects
16277  * 
16278  * @cfg {Array} fields Array of field definition objects
16279  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16280  * as specified to {@link Roo.data.Record#create},
16281  * or an {@link Roo.data.Record} object
16282  *
16283  * 
16284  * created using {@link Roo.data.Record#create}.
16285  */
16286 Roo.data.ArrayReader = function(meta, recordType)
16287 {    
16288     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16289 };
16290
16291 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16292     
16293       /**
16294      * Create a data block containing Roo.data.Records from an XML document.
16295      * @param {Object} o An Array of row objects which represents the dataset.
16296      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16297      * a cache of Roo.data.Records.
16298      */
16299     readRecords : function(o)
16300     {
16301         var sid = this.meta ? this.meta.id : null;
16302         var recordType = this.recordType, fields = recordType.prototype.fields;
16303         var records = [];
16304         var root = o;
16305         for(var i = 0; i < root.length; i++){
16306             var n = root[i];
16307             var values = {};
16308             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16309             for(var j = 0, jlen = fields.length; j < jlen; j++){
16310                 var f = fields.items[j];
16311                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16312                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16313                 v = f.convert(v);
16314                 values[f.name] = v;
16315             }
16316             var record = new recordType(values, id);
16317             record.json = n;
16318             records[records.length] = record;
16319         }
16320         return {
16321             records : records,
16322             totalRecords : records.length
16323         };
16324     },
16325     // used when loading children.. @see loadDataFromChildren
16326     toLoadData: function(rec)
16327     {
16328         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16329         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16330         
16331     }
16332     
16333     
16334 });/*
16335  * - LGPL
16336  * * 
16337  */
16338
16339 /**
16340  * @class Roo.bootstrap.ComboBox
16341  * @extends Roo.bootstrap.TriggerField
16342  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16343  * @cfg {Boolean} append (true|false) default false
16344  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16345  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16346  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16347  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16348  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16349  * @cfg {Boolean} animate default true
16350  * @cfg {Boolean} emptyResultText only for touch device
16351  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16352  * @cfg {String} emptyTitle default ''
16353  * @cfg {Number} width fixed with? experimental
16354  * @constructor
16355  * Create a new ComboBox.
16356  * @param {Object} config Configuration options
16357  */
16358 Roo.bootstrap.ComboBox = function(config){
16359     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16360     this.addEvents({
16361         /**
16362          * @event expand
16363          * Fires when the dropdown list is expanded
16364         * @param {Roo.bootstrap.ComboBox} combo This combo box
16365         */
16366         'expand' : true,
16367         /**
16368          * @event collapse
16369          * Fires when the dropdown list is collapsed
16370         * @param {Roo.bootstrap.ComboBox} combo This combo box
16371         */
16372         'collapse' : true,
16373         /**
16374          * @event beforeselect
16375          * Fires before a list item is selected. Return false to cancel the selection.
16376         * @param {Roo.bootstrap.ComboBox} combo This combo box
16377         * @param {Roo.data.Record} record The data record returned from the underlying store
16378         * @param {Number} index The index of the selected item in the dropdown list
16379         */
16380         'beforeselect' : true,
16381         /**
16382          * @event select
16383          * Fires when a list item is selected
16384         * @param {Roo.bootstrap.ComboBox} combo This combo box
16385         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16386         * @param {Number} index The index of the selected item in the dropdown list
16387         */
16388         'select' : true,
16389         /**
16390          * @event beforequery
16391          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16392          * The event object passed has these properties:
16393         * @param {Roo.bootstrap.ComboBox} combo This combo box
16394         * @param {String} query The query
16395         * @param {Boolean} forceAll true to force "all" query
16396         * @param {Boolean} cancel true to cancel the query
16397         * @param {Object} e The query event object
16398         */
16399         'beforequery': true,
16400          /**
16401          * @event add
16402          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16403         * @param {Roo.bootstrap.ComboBox} combo This combo box
16404         */
16405         'add' : true,
16406         /**
16407          * @event edit
16408          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16411         */
16412         'edit' : true,
16413         /**
16414          * @event remove
16415          * Fires when the remove value from the combobox array
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         */
16418         'remove' : true,
16419         /**
16420          * @event afterremove
16421          * Fires when the remove value from the combobox array
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         */
16424         'afterremove' : true,
16425         /**
16426          * @event specialfilter
16427          * Fires when specialfilter
16428             * @param {Roo.bootstrap.ComboBox} combo This combo box
16429             */
16430         'specialfilter' : true,
16431         /**
16432          * @event tick
16433          * Fires when tick the element
16434             * @param {Roo.bootstrap.ComboBox} combo This combo box
16435             */
16436         'tick' : true,
16437         /**
16438          * @event touchviewdisplay
16439          * Fires when touch view require special display (default is using displayField)
16440             * @param {Roo.bootstrap.ComboBox} combo This combo box
16441             * @param {Object} cfg set html .
16442             */
16443         'touchviewdisplay' : true
16444         
16445     });
16446     
16447     this.item = [];
16448     this.tickItems = [];
16449     
16450     this.selectedIndex = -1;
16451     if(this.mode == 'local'){
16452         if(config.queryDelay === undefined){
16453             this.queryDelay = 10;
16454         }
16455         if(config.minChars === undefined){
16456             this.minChars = 0;
16457         }
16458     }
16459 };
16460
16461 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16462      
16463     /**
16464      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16465      * rendering into an Roo.Editor, defaults to false)
16466      */
16467     /**
16468      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16469      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16470      */
16471     /**
16472      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16473      */
16474     /**
16475      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16476      * the dropdown list (defaults to undefined, with no header element)
16477      */
16478
16479      /**
16480      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16481      */
16482      
16483      /**
16484      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16485      */
16486     listWidth: undefined,
16487     /**
16488      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16489      * mode = 'remote' or 'text' if mode = 'local')
16490      */
16491     displayField: undefined,
16492     
16493     /**
16494      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16495      * mode = 'remote' or 'value' if mode = 'local'). 
16496      * Note: use of a valueField requires the user make a selection
16497      * in order for a value to be mapped.
16498      */
16499     valueField: undefined,
16500     /**
16501      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16502      */
16503     modalTitle : '',
16504     
16505     /**
16506      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16507      * field's data value (defaults to the underlying DOM element's name)
16508      */
16509     hiddenName: undefined,
16510     /**
16511      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16512      */
16513     listClass: '',
16514     /**
16515      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16516      */
16517     selectedClass: 'active',
16518     
16519     /**
16520      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16521      */
16522     shadow:'sides',
16523     /**
16524      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16525      * anchor positions (defaults to 'tl-bl')
16526      */
16527     listAlign: 'tl-bl?',
16528     /**
16529      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16530      */
16531     maxHeight: 300,
16532     /**
16533      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16534      * query specified by the allQuery config option (defaults to 'query')
16535      */
16536     triggerAction: 'query',
16537     /**
16538      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16539      * (defaults to 4, does not apply if editable = false)
16540      */
16541     minChars : 4,
16542     /**
16543      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16544      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16545      */
16546     typeAhead: false,
16547     /**
16548      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16549      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16550      */
16551     queryDelay: 500,
16552     /**
16553      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16554      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16555      */
16556     pageSize: 0,
16557     /**
16558      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16559      * when editable = true (defaults to false)
16560      */
16561     selectOnFocus:false,
16562     /**
16563      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16564      */
16565     queryParam: 'query',
16566     /**
16567      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16568      * when mode = 'remote' (defaults to 'Loading...')
16569      */
16570     loadingText: 'Loading...',
16571     /**
16572      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16573      */
16574     resizable: false,
16575     /**
16576      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16577      */
16578     handleHeight : 8,
16579     /**
16580      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16581      * traditional select (defaults to true)
16582      */
16583     editable: true,
16584     /**
16585      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16586      */
16587     allQuery: '',
16588     /**
16589      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16590      */
16591     mode: 'remote',
16592     /**
16593      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16594      * listWidth has a higher value)
16595      */
16596     minListWidth : 70,
16597     /**
16598      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16599      * allow the user to set arbitrary text into the field (defaults to false)
16600      */
16601     forceSelection:false,
16602     /**
16603      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16604      * if typeAhead = true (defaults to 250)
16605      */
16606     typeAheadDelay : 250,
16607     /**
16608      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16609      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16610      */
16611     valueNotFoundText : undefined,
16612     /**
16613      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16614      */
16615     blockFocus : false,
16616     
16617     /**
16618      * @cfg {Boolean} disableClear Disable showing of clear button.
16619      */
16620     disableClear : false,
16621     /**
16622      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16623      */
16624     alwaysQuery : false,
16625     
16626     /**
16627      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16628      */
16629     multiple : false,
16630     
16631     /**
16632      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16633      */
16634     invalidClass : "has-warning",
16635     
16636     /**
16637      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16638      */
16639     validClass : "has-success",
16640     
16641     /**
16642      * @cfg {Boolean} specialFilter (true|false) special filter default false
16643      */
16644     specialFilter : false,
16645     
16646     /**
16647      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16648      */
16649     mobileTouchView : true,
16650     
16651     /**
16652      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16653      */
16654     useNativeIOS : false,
16655     
16656     /**
16657      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16658      */
16659     mobile_restrict_height : false,
16660     
16661     ios_options : false,
16662     
16663     //private
16664     addicon : false,
16665     editicon: false,
16666     
16667     page: 0,
16668     hasQuery: false,
16669     append: false,
16670     loadNext: false,
16671     autoFocus : true,
16672     tickable : false,
16673     btnPosition : 'right',
16674     triggerList : true,
16675     showToggleBtn : true,
16676     animate : true,
16677     emptyResultText: 'Empty',
16678     triggerText : 'Select',
16679     emptyTitle : '',
16680     width : false,
16681     
16682     // element that contains real text value.. (when hidden is used..)
16683     
16684     getAutoCreate : function()
16685     {   
16686         var cfg = false;
16687         //render
16688         /*
16689          * Render classic select for iso
16690          */
16691         
16692         if(Roo.isIOS && this.useNativeIOS){
16693             cfg = this.getAutoCreateNativeIOS();
16694             return cfg;
16695         }
16696         
16697         /*
16698          * Touch Devices
16699          */
16700         
16701         if(Roo.isTouch && this.mobileTouchView){
16702             cfg = this.getAutoCreateTouchView();
16703             return cfg;;
16704         }
16705         
16706         /*
16707          *  Normal ComboBox
16708          */
16709         if(!this.tickable){
16710             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16711             return cfg;
16712         }
16713         
16714         /*
16715          *  ComboBox with tickable selections
16716          */
16717              
16718         var align = this.labelAlign || this.parentLabelAlign();
16719         
16720         cfg = {
16721             cls : 'form-group roo-combobox-tickable' //input-group
16722         };
16723         
16724         var btn_text_select = '';
16725         var btn_text_done = '';
16726         var btn_text_cancel = '';
16727         
16728         if (this.btn_text_show) {
16729             btn_text_select = 'Select';
16730             btn_text_done = 'Done';
16731             btn_text_cancel = 'Cancel'; 
16732         }
16733         
16734         var buttons = {
16735             tag : 'div',
16736             cls : 'tickable-buttons',
16737             cn : [
16738                 {
16739                     tag : 'button',
16740                     type : 'button',
16741                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16742                     //html : this.triggerText
16743                     html: btn_text_select
16744                 },
16745                 {
16746                     tag : 'button',
16747                     type : 'button',
16748                     name : 'ok',
16749                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16750                     //html : 'Done'
16751                     html: btn_text_done
16752                 },
16753                 {
16754                     tag : 'button',
16755                     type : 'button',
16756                     name : 'cancel',
16757                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16758                     //html : 'Cancel'
16759                     html: btn_text_cancel
16760                 }
16761             ]
16762         };
16763         
16764         if(this.editable){
16765             buttons.cn.unshift({
16766                 tag: 'input',
16767                 cls: 'roo-select2-search-field-input'
16768             });
16769         }
16770         
16771         var _this = this;
16772         
16773         Roo.each(buttons.cn, function(c){
16774             if (_this.size) {
16775                 c.cls += ' btn-' + _this.size;
16776             }
16777
16778             if (_this.disabled) {
16779                 c.disabled = true;
16780             }
16781         });
16782         
16783         var box = {
16784             tag: 'div',
16785             style : 'display: contents',
16786             cn: [
16787                 {
16788                     tag: 'input',
16789                     type : 'hidden',
16790                     cls: 'form-hidden-field'
16791                 },
16792                 {
16793                     tag: 'ul',
16794                     cls: 'roo-select2-choices',
16795                     cn:[
16796                         {
16797                             tag: 'li',
16798                             cls: 'roo-select2-search-field',
16799                             cn: [
16800                                 buttons
16801                             ]
16802                         }
16803                     ]
16804                 }
16805             ]
16806         };
16807         
16808         var combobox = {
16809             cls: 'roo-select2-container input-group roo-select2-container-multi',
16810             cn: [
16811                 
16812                 box
16813 //                {
16814 //                    tag: 'ul',
16815 //                    cls: 'typeahead typeahead-long dropdown-menu',
16816 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16817 //                }
16818             ]
16819         };
16820         
16821         if(this.hasFeedback && !this.allowBlank){
16822             
16823             var feedback = {
16824                 tag: 'span',
16825                 cls: 'glyphicon form-control-feedback'
16826             };
16827
16828             combobox.cn.push(feedback);
16829         }
16830         
16831         
16832         
16833         var indicator = {
16834             tag : 'i',
16835             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16836             tooltip : 'This field is required'
16837         };
16838         if (Roo.bootstrap.version == 4) {
16839             indicator = {
16840                 tag : 'i',
16841                 style : 'display:none'
16842             };
16843         }
16844         if (align ==='left' && this.fieldLabel.length) {
16845             
16846             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16847             
16848             cfg.cn = [
16849                 indicator,
16850                 {
16851                     tag: 'label',
16852                     'for' :  id,
16853                     cls : 'control-label col-form-label',
16854                     html : this.fieldLabel
16855
16856                 },
16857                 {
16858                     cls : "", 
16859                     cn: [
16860                         combobox
16861                     ]
16862                 }
16863
16864             ];
16865             
16866             var labelCfg = cfg.cn[1];
16867             var contentCfg = cfg.cn[2];
16868             
16869
16870             if(this.indicatorpos == 'right'){
16871                 
16872                 cfg.cn = [
16873                     {
16874                         tag: 'label',
16875                         'for' :  id,
16876                         cls : 'control-label col-form-label',
16877                         cn : [
16878                             {
16879                                 tag : 'span',
16880                                 html : this.fieldLabel
16881                             },
16882                             indicator
16883                         ]
16884                     },
16885                     {
16886                         cls : "",
16887                         cn: [
16888                             combobox
16889                         ]
16890                     }
16891
16892                 ];
16893                 
16894                 
16895                 
16896                 labelCfg = cfg.cn[0];
16897                 contentCfg = cfg.cn[1];
16898             
16899             }
16900             
16901             if(this.labelWidth > 12){
16902                 labelCfg.style = "width: " + this.labelWidth + 'px';
16903             }
16904             if(this.width * 1 > 0){
16905                 contentCfg.style = "width: " + this.width + 'px';
16906             }
16907             if(this.labelWidth < 13 && this.labelmd == 0){
16908                 this.labelmd = this.labelWidth;
16909             }
16910             
16911             if(this.labellg > 0){
16912                 labelCfg.cls += ' col-lg-' + this.labellg;
16913                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16914             }
16915             
16916             if(this.labelmd > 0){
16917                 labelCfg.cls += ' col-md-' + this.labelmd;
16918                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16919             }
16920             
16921             if(this.labelsm > 0){
16922                 labelCfg.cls += ' col-sm-' + this.labelsm;
16923                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16924             }
16925             
16926             if(this.labelxs > 0){
16927                 labelCfg.cls += ' col-xs-' + this.labelxs;
16928                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16929             }
16930                 
16931                 
16932         } else if ( this.fieldLabel.length) {
16933 //                Roo.log(" label");
16934                  cfg.cn = [
16935                    indicator,
16936                     {
16937                         tag: 'label',
16938                         //cls : 'input-group-addon',
16939                         html : this.fieldLabel
16940                     },
16941                     combobox
16942                 ];
16943                 
16944                 if(this.indicatorpos == 'right'){
16945                     cfg.cn = [
16946                         {
16947                             tag: 'label',
16948                             //cls : 'input-group-addon',
16949                             html : this.fieldLabel
16950                         },
16951                         indicator,
16952                         combobox
16953                     ];
16954                     
16955                 }
16956
16957         } else {
16958             
16959 //                Roo.log(" no label && no align");
16960                 cfg = combobox
16961                      
16962                 
16963         }
16964          
16965         var settings=this;
16966         ['xs','sm','md','lg'].map(function(size){
16967             if (settings[size]) {
16968                 cfg.cls += ' col-' + size + '-' + settings[size];
16969             }
16970         });
16971         
16972         return cfg;
16973         
16974     },
16975     
16976     _initEventsCalled : false,
16977     
16978     // private
16979     initEvents: function()
16980     {   
16981         if (this._initEventsCalled) { // as we call render... prevent looping...
16982             return;
16983         }
16984         this._initEventsCalled = true;
16985         
16986         if (!this.store) {
16987             throw "can not find store for combo";
16988         }
16989         
16990         this.indicator = this.indicatorEl();
16991         
16992         this.store = Roo.factory(this.store, Roo.data);
16993         this.store.parent = this;
16994         
16995         // if we are building from html. then this element is so complex, that we can not really
16996         // use the rendered HTML.
16997         // so we have to trash and replace the previous code.
16998         if (Roo.XComponent.build_from_html) {
16999             // remove this element....
17000             var e = this.el.dom, k=0;
17001             while (e ) { e = e.previousSibling;  ++k;}
17002
17003             this.el.remove();
17004             
17005             this.el=false;
17006             this.rendered = false;
17007             
17008             this.render(this.parent().getChildContainer(true), k);
17009         }
17010         
17011         if(Roo.isIOS && this.useNativeIOS){
17012             this.initIOSView();
17013             return;
17014         }
17015         
17016         /*
17017          * Touch Devices
17018          */
17019         
17020         if(Roo.isTouch && this.mobileTouchView){
17021             this.initTouchView();
17022             return;
17023         }
17024         
17025         if(this.tickable){
17026             this.initTickableEvents();
17027             return;
17028         }
17029         
17030         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17031         
17032         if(this.hiddenName){
17033             
17034             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17035             
17036             this.hiddenField.dom.value =
17037                 this.hiddenValue !== undefined ? this.hiddenValue :
17038                 this.value !== undefined ? this.value : '';
17039
17040             // prevent input submission
17041             this.el.dom.removeAttribute('name');
17042             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17043              
17044              
17045         }
17046         //if(Roo.isGecko){
17047         //    this.el.dom.setAttribute('autocomplete', 'off');
17048         //}
17049         
17050         var cls = 'x-combo-list';
17051         
17052         //this.list = new Roo.Layer({
17053         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17054         //});
17055         
17056         var _this = this;
17057         
17058         (function(){
17059             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17060             _this.list.setWidth(lw);
17061         }).defer(100);
17062         
17063         this.list.on('mouseover', this.onViewOver, this);
17064         this.list.on('mousemove', this.onViewMove, this);
17065         this.list.on('scroll', this.onViewScroll, this);
17066         
17067         /*
17068         this.list.swallowEvent('mousewheel');
17069         this.assetHeight = 0;
17070
17071         if(this.title){
17072             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17073             this.assetHeight += this.header.getHeight();
17074         }
17075
17076         this.innerList = this.list.createChild({cls:cls+'-inner'});
17077         this.innerList.on('mouseover', this.onViewOver, this);
17078         this.innerList.on('mousemove', this.onViewMove, this);
17079         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17080         
17081         if(this.allowBlank && !this.pageSize && !this.disableClear){
17082             this.footer = this.list.createChild({cls:cls+'-ft'});
17083             this.pageTb = new Roo.Toolbar(this.footer);
17084            
17085         }
17086         if(this.pageSize){
17087             this.footer = this.list.createChild({cls:cls+'-ft'});
17088             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17089                     {pageSize: this.pageSize});
17090             
17091         }
17092         
17093         if (this.pageTb && this.allowBlank && !this.disableClear) {
17094             var _this = this;
17095             this.pageTb.add(new Roo.Toolbar.Fill(), {
17096                 cls: 'x-btn-icon x-btn-clear',
17097                 text: '&#160;',
17098                 handler: function()
17099                 {
17100                     _this.collapse();
17101                     _this.clearValue();
17102                     _this.onSelect(false, -1);
17103                 }
17104             });
17105         }
17106         if (this.footer) {
17107             this.assetHeight += this.footer.getHeight();
17108         }
17109         */
17110             
17111         if(!this.tpl){
17112             this.tpl = Roo.bootstrap.version == 4 ?
17113                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17114                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17115         }
17116
17117         this.view = new Roo.View(this.list, this.tpl, {
17118             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17119         });
17120         //this.view.wrapEl.setDisplayed(false);
17121         this.view.on('click', this.onViewClick, this);
17122         
17123         
17124         this.store.on('beforeload', this.onBeforeLoad, this);
17125         this.store.on('load', this.onLoad, this);
17126         this.store.on('loadexception', this.onLoadException, this);
17127         /*
17128         if(this.resizable){
17129             this.resizer = new Roo.Resizable(this.list,  {
17130                pinned:true, handles:'se'
17131             });
17132             this.resizer.on('resize', function(r, w, h){
17133                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17134                 this.listWidth = w;
17135                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17136                 this.restrictHeight();
17137             }, this);
17138             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17139         }
17140         */
17141         if(!this.editable){
17142             this.editable = true;
17143             this.setEditable(false);
17144         }
17145         
17146         /*
17147         
17148         if (typeof(this.events.add.listeners) != 'undefined') {
17149             
17150             this.addicon = this.wrap.createChild(
17151                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17152        
17153             this.addicon.on('click', function(e) {
17154                 this.fireEvent('add', this);
17155             }, this);
17156         }
17157         if (typeof(this.events.edit.listeners) != 'undefined') {
17158             
17159             this.editicon = this.wrap.createChild(
17160                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17161             if (this.addicon) {
17162                 this.editicon.setStyle('margin-left', '40px');
17163             }
17164             this.editicon.on('click', function(e) {
17165                 
17166                 // we fire even  if inothing is selected..
17167                 this.fireEvent('edit', this, this.lastData );
17168                 
17169             }, this);
17170         }
17171         */
17172         
17173         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17174             "up" : function(e){
17175                 this.inKeyMode = true;
17176                 this.selectPrev();
17177             },
17178
17179             "down" : function(e){
17180                 if(!this.isExpanded()){
17181                     this.onTriggerClick();
17182                 }else{
17183                     this.inKeyMode = true;
17184                     this.selectNext();
17185                 }
17186             },
17187
17188             "enter" : function(e){
17189 //                this.onViewClick();
17190                 //return true;
17191                 this.collapse();
17192                 
17193                 if(this.fireEvent("specialkey", this, e)){
17194                     this.onViewClick(false);
17195                 }
17196                 
17197                 return true;
17198             },
17199
17200             "esc" : function(e){
17201                 this.collapse();
17202             },
17203
17204             "tab" : function(e){
17205                 this.collapse();
17206                 
17207                 if(this.fireEvent("specialkey", this, e)){
17208                     this.onViewClick(false);
17209                 }
17210                 
17211                 return true;
17212             },
17213
17214             scope : this,
17215
17216             doRelay : function(foo, bar, hname){
17217                 if(hname == 'down' || this.scope.isExpanded()){
17218                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17219                 }
17220                 return true;
17221             },
17222
17223             forceKeyDown: true
17224         });
17225         
17226         
17227         this.queryDelay = Math.max(this.queryDelay || 10,
17228                 this.mode == 'local' ? 10 : 250);
17229         
17230         
17231         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17232         
17233         if(this.typeAhead){
17234             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17235         }
17236         if(this.editable !== false){
17237             this.inputEl().on("keyup", this.onKeyUp, this);
17238         }
17239         if(this.forceSelection){
17240             this.inputEl().on('blur', this.doForce, this);
17241         }
17242         
17243         if(this.multiple){
17244             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17245             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17246         }
17247     },
17248     
17249     initTickableEvents: function()
17250     {   
17251         this.createList();
17252         
17253         if(this.hiddenName){
17254             
17255             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17256             
17257             this.hiddenField.dom.value =
17258                 this.hiddenValue !== undefined ? this.hiddenValue :
17259                 this.value !== undefined ? this.value : '';
17260
17261             // prevent input submission
17262             this.el.dom.removeAttribute('name');
17263             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17264              
17265              
17266         }
17267         
17268 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17269         
17270         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17271         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17272         if(this.triggerList){
17273             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17274         }
17275          
17276         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17277         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17278         
17279         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17280         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17281         
17282         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17283         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17284         
17285         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17286         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17287         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17288         
17289         this.okBtn.hide();
17290         this.cancelBtn.hide();
17291         
17292         var _this = this;
17293         
17294         (function(){
17295             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17296             _this.list.setWidth(lw);
17297         }).defer(100);
17298         
17299         this.list.on('mouseover', this.onViewOver, this);
17300         this.list.on('mousemove', this.onViewMove, this);
17301         
17302         this.list.on('scroll', this.onViewScroll, this);
17303         
17304         if(!this.tpl){
17305             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17306                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17307         }
17308
17309         this.view = new Roo.View(this.list, this.tpl, {
17310             singleSelect:true,
17311             tickable:true,
17312             parent:this,
17313             store: this.store,
17314             selectedClass: this.selectedClass
17315         });
17316         
17317         //this.view.wrapEl.setDisplayed(false);
17318         this.view.on('click', this.onViewClick, this);
17319         
17320         
17321         
17322         this.store.on('beforeload', this.onBeforeLoad, this);
17323         this.store.on('load', this.onLoad, this);
17324         this.store.on('loadexception', this.onLoadException, this);
17325         
17326         if(this.editable){
17327             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17328                 "up" : function(e){
17329                     this.inKeyMode = true;
17330                     this.selectPrev();
17331                 },
17332
17333                 "down" : function(e){
17334                     this.inKeyMode = true;
17335                     this.selectNext();
17336                 },
17337
17338                 "enter" : function(e){
17339                     if(this.fireEvent("specialkey", this, e)){
17340                         this.onViewClick(false);
17341                     }
17342                     
17343                     return true;
17344                 },
17345
17346                 "esc" : function(e){
17347                     this.onTickableFooterButtonClick(e, false, false);
17348                 },
17349
17350                 "tab" : function(e){
17351                     this.fireEvent("specialkey", this, e);
17352                     
17353                     this.onTickableFooterButtonClick(e, false, false);
17354                     
17355                     return true;
17356                 },
17357
17358                 scope : this,
17359
17360                 doRelay : function(e, fn, key){
17361                     if(this.scope.isExpanded()){
17362                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17363                     }
17364                     return true;
17365                 },
17366
17367                 forceKeyDown: true
17368             });
17369         }
17370         
17371         this.queryDelay = Math.max(this.queryDelay || 10,
17372                 this.mode == 'local' ? 10 : 250);
17373         
17374         
17375         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17376         
17377         if(this.typeAhead){
17378             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17379         }
17380         
17381         if(this.editable !== false){
17382             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17383         }
17384         
17385         this.indicator = this.indicatorEl();
17386         
17387         if(this.indicator){
17388             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17389             this.indicator.hide();
17390         }
17391         
17392     },
17393
17394     onDestroy : function(){
17395         if(this.view){
17396             this.view.setStore(null);
17397             this.view.el.removeAllListeners();
17398             this.view.el.remove();
17399             this.view.purgeListeners();
17400         }
17401         if(this.list){
17402             this.list.dom.innerHTML  = '';
17403         }
17404         
17405         if(this.store){
17406             this.store.un('beforeload', this.onBeforeLoad, this);
17407             this.store.un('load', this.onLoad, this);
17408             this.store.un('loadexception', this.onLoadException, this);
17409         }
17410         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17411     },
17412
17413     // private
17414     fireKey : function(e){
17415         if(e.isNavKeyPress() && !this.list.isVisible()){
17416             this.fireEvent("specialkey", this, e);
17417         }
17418     },
17419
17420     // private
17421     onResize: function(w, h)
17422     {
17423         
17424         
17425 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17426 //        
17427 //        if(typeof w != 'number'){
17428 //            // we do not handle it!?!?
17429 //            return;
17430 //        }
17431 //        var tw = this.trigger.getWidth();
17432 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17433 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17434 //        var x = w - tw;
17435 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17436 //            
17437 //        //this.trigger.setStyle('left', x+'px');
17438 //        
17439 //        if(this.list && this.listWidth === undefined){
17440 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17441 //            this.list.setWidth(lw);
17442 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17443 //        }
17444         
17445     
17446         
17447     },
17448
17449     /**
17450      * Allow or prevent the user from directly editing the field text.  If false is passed,
17451      * the user will only be able to select from the items defined in the dropdown list.  This method
17452      * is the runtime equivalent of setting the 'editable' config option at config time.
17453      * @param {Boolean} value True to allow the user to directly edit the field text
17454      */
17455     setEditable : function(value){
17456         if(value == this.editable){
17457             return;
17458         }
17459         this.editable = value;
17460         if(!value){
17461             this.inputEl().dom.setAttribute('readOnly', true);
17462             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17463             this.inputEl().addClass('x-combo-noedit');
17464         }else{
17465             this.inputEl().dom.removeAttribute('readOnly');
17466             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17467             this.inputEl().removeClass('x-combo-noedit');
17468         }
17469     },
17470
17471     // private
17472     
17473     onBeforeLoad : function(combo,opts){
17474         if(!this.hasFocus){
17475             return;
17476         }
17477          if (!opts.add) {
17478             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17479          }
17480         this.restrictHeight();
17481         this.selectedIndex = -1;
17482     },
17483
17484     // private
17485     onLoad : function(){
17486         
17487         this.hasQuery = false;
17488         
17489         if(!this.hasFocus){
17490             return;
17491         }
17492         
17493         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17494             this.loading.hide();
17495         }
17496         
17497         if(this.store.getCount() > 0){
17498             
17499             this.expand();
17500             this.restrictHeight();
17501             if(this.lastQuery == this.allQuery){
17502                 if(this.editable && !this.tickable){
17503                     this.inputEl().dom.select();
17504                 }
17505                 
17506                 if(
17507                     !this.selectByValue(this.value, true) &&
17508                     this.autoFocus && 
17509                     (
17510                         !this.store.lastOptions ||
17511                         typeof(this.store.lastOptions.add) == 'undefined' || 
17512                         this.store.lastOptions.add != true
17513                     )
17514                 ){
17515                     this.select(0, true);
17516                 }
17517             }else{
17518                 if(this.autoFocus){
17519                     this.selectNext();
17520                 }
17521                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17522                     this.taTask.delay(this.typeAheadDelay);
17523                 }
17524             }
17525         }else{
17526             this.onEmptyResults();
17527         }
17528         
17529         //this.el.focus();
17530     },
17531     // private
17532     onLoadException : function()
17533     {
17534         this.hasQuery = false;
17535         
17536         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17537             this.loading.hide();
17538         }
17539         
17540         if(this.tickable && this.editable){
17541             return;
17542         }
17543         
17544         this.collapse();
17545         // only causes errors at present
17546         //Roo.log(this.store.reader.jsonData);
17547         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17548             // fixme
17549             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17550         //}
17551         
17552         
17553     },
17554     // private
17555     onTypeAhead : function(){
17556         if(this.store.getCount() > 0){
17557             var r = this.store.getAt(0);
17558             var newValue = r.data[this.displayField];
17559             var len = newValue.length;
17560             var selStart = this.getRawValue().length;
17561             
17562             if(selStart != len){
17563                 this.setRawValue(newValue);
17564                 this.selectText(selStart, newValue.length);
17565             }
17566         }
17567     },
17568
17569     // private
17570     onSelect : function(record, index){
17571         
17572         if(this.fireEvent('beforeselect', this, record, index) !== false){
17573         
17574             this.setFromData(index > -1 ? record.data : false);
17575             
17576             this.collapse();
17577             this.fireEvent('select', this, record, index);
17578         }
17579     },
17580
17581     /**
17582      * Returns the currently selected field value or empty string if no value is set.
17583      * @return {String} value The selected value
17584      */
17585     getValue : function()
17586     {
17587         if(Roo.isIOS && this.useNativeIOS){
17588             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17589         }
17590         
17591         if(this.multiple){
17592             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17593         }
17594         
17595         if(this.valueField){
17596             return typeof this.value != 'undefined' ? this.value : '';
17597         }else{
17598             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17599         }
17600     },
17601     
17602     getRawValue : function()
17603     {
17604         if(Roo.isIOS && this.useNativeIOS){
17605             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17606         }
17607         
17608         var v = this.inputEl().getValue();
17609         
17610         return v;
17611     },
17612
17613     /**
17614      * Clears any text/value currently set in the field
17615      */
17616     clearValue : function(){
17617         
17618         if(this.hiddenField){
17619             this.hiddenField.dom.value = '';
17620         }
17621         this.value = '';
17622         this.setRawValue('');
17623         this.lastSelectionText = '';
17624         this.lastData = false;
17625         
17626         var close = this.closeTriggerEl();
17627         
17628         if(close){
17629             close.hide();
17630         }
17631         
17632         this.validate();
17633         
17634     },
17635
17636     /**
17637      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17638      * will be displayed in the field.  If the value does not match the data value of an existing item,
17639      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17640      * Otherwise the field will be blank (although the value will still be set).
17641      * @param {String} value The value to match
17642      */
17643     setValue : function(v)
17644     {
17645         if(Roo.isIOS && this.useNativeIOS){
17646             this.setIOSValue(v);
17647             return;
17648         }
17649         
17650         if(this.multiple){
17651             this.syncValue();
17652             return;
17653         }
17654         
17655         var text = v;
17656         if(this.valueField){
17657             var r = this.findRecord(this.valueField, v);
17658             if(r){
17659                 text = r.data[this.displayField];
17660             }else if(this.valueNotFoundText !== undefined){
17661                 text = this.valueNotFoundText;
17662             }
17663         }
17664         this.lastSelectionText = text;
17665         if(this.hiddenField){
17666             this.hiddenField.dom.value = v;
17667         }
17668         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17669         this.value = v;
17670         
17671         var close = this.closeTriggerEl();
17672         
17673         if(close){
17674             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17675         }
17676         
17677         this.validate();
17678     },
17679     /**
17680      * @property {Object} the last set data for the element
17681      */
17682     
17683     lastData : false,
17684     /**
17685      * Sets the value of the field based on a object which is related to the record format for the store.
17686      * @param {Object} value the value to set as. or false on reset?
17687      */
17688     setFromData : function(o){
17689         
17690         if(this.multiple){
17691             this.addItem(o);
17692             return;
17693         }
17694             
17695         var dv = ''; // display value
17696         var vv = ''; // value value..
17697         this.lastData = o;
17698         if (this.displayField) {
17699             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17700         } else {
17701             // this is an error condition!!!
17702             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17703         }
17704         
17705         if(this.valueField){
17706             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17707         }
17708         
17709         var close = this.closeTriggerEl();
17710         
17711         if(close){
17712             if(dv.length || vv * 1 > 0){
17713                 close.show() ;
17714                 this.blockFocus=true;
17715             } else {
17716                 close.hide();
17717             }             
17718         }
17719         
17720         if(this.hiddenField){
17721             this.hiddenField.dom.value = vv;
17722             
17723             this.lastSelectionText = dv;
17724             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17725             this.value = vv;
17726             return;
17727         }
17728         // no hidden field.. - we store the value in 'value', but still display
17729         // display field!!!!
17730         this.lastSelectionText = dv;
17731         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17732         this.value = vv;
17733         
17734         
17735         
17736     },
17737     // private
17738     reset : function(){
17739         // overridden so that last data is reset..
17740         
17741         if(this.multiple){
17742             this.clearItem();
17743             return;
17744         }
17745         
17746         this.setValue(this.originalValue);
17747         //this.clearInvalid();
17748         this.lastData = false;
17749         if (this.view) {
17750             this.view.clearSelections();
17751         }
17752         
17753         this.validate();
17754     },
17755     // private
17756     findRecord : function(prop, value){
17757         var record;
17758         if(this.store.getCount() > 0){
17759             this.store.each(function(r){
17760                 if(r.data[prop] == value){
17761                     record = r;
17762                     return false;
17763                 }
17764                 return true;
17765             });
17766         }
17767         return record;
17768     },
17769     
17770     getName: function()
17771     {
17772         // returns hidden if it's set..
17773         if (!this.rendered) {return ''};
17774         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17775         
17776     },
17777     // private
17778     onViewMove : function(e, t){
17779         this.inKeyMode = false;
17780     },
17781
17782     // private
17783     onViewOver : function(e, t){
17784         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17785             return;
17786         }
17787         var item = this.view.findItemFromChild(t);
17788         
17789         if(item){
17790             var index = this.view.indexOf(item);
17791             this.select(index, false);
17792         }
17793     },
17794
17795     // private
17796     onViewClick : function(view, doFocus, el, e)
17797     {
17798         var index = this.view.getSelectedIndexes()[0];
17799         
17800         var r = this.store.getAt(index);
17801         
17802         if(this.tickable){
17803             
17804             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17805                 return;
17806             }
17807             
17808             var rm = false;
17809             var _this = this;
17810             
17811             Roo.each(this.tickItems, function(v,k){
17812                 
17813                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17814                     Roo.log(v);
17815                     _this.tickItems.splice(k, 1);
17816                     
17817                     if(typeof(e) == 'undefined' && view == false){
17818                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17819                     }
17820                     
17821                     rm = true;
17822                     return;
17823                 }
17824             });
17825             
17826             if(rm){
17827                 return;
17828             }
17829             
17830             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17831                 this.tickItems.push(r.data);
17832             }
17833             
17834             if(typeof(e) == 'undefined' && view == false){
17835                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17836             }
17837                     
17838             return;
17839         }
17840         
17841         if(r){
17842             this.onSelect(r, index);
17843         }
17844         if(doFocus !== false && !this.blockFocus){
17845             this.inputEl().focus();
17846         }
17847     },
17848
17849     // private
17850     restrictHeight : function(){
17851         //this.innerList.dom.style.height = '';
17852         //var inner = this.innerList.dom;
17853         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17854         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17855         //this.list.beginUpdate();
17856         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17857         this.list.alignTo(this.inputEl(), this.listAlign);
17858         this.list.alignTo(this.inputEl(), this.listAlign);
17859         //this.list.endUpdate();
17860     },
17861
17862     // private
17863     onEmptyResults : function(){
17864         
17865         if(this.tickable && this.editable){
17866             this.hasFocus = false;
17867             this.restrictHeight();
17868             return;
17869         }
17870         
17871         this.collapse();
17872     },
17873
17874     /**
17875      * Returns true if the dropdown list is expanded, else false.
17876      */
17877     isExpanded : function(){
17878         return this.list.isVisible();
17879     },
17880
17881     /**
17882      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17883      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17884      * @param {String} value The data value of the item to select
17885      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17886      * selected item if it is not currently in view (defaults to true)
17887      * @return {Boolean} True if the value matched an item in the list, else false
17888      */
17889     selectByValue : function(v, scrollIntoView){
17890         if(v !== undefined && v !== null){
17891             var r = this.findRecord(this.valueField || this.displayField, v);
17892             if(r){
17893                 this.select(this.store.indexOf(r), scrollIntoView);
17894                 return true;
17895             }
17896         }
17897         return false;
17898     },
17899
17900     /**
17901      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17902      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17903      * @param {Number} index The zero-based index of the list item to select
17904      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17905      * selected item if it is not currently in view (defaults to true)
17906      */
17907     select : function(index, scrollIntoView){
17908         this.selectedIndex = index;
17909         this.view.select(index);
17910         if(scrollIntoView !== false){
17911             var el = this.view.getNode(index);
17912             /*
17913              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17914              */
17915             if(el){
17916                 this.list.scrollChildIntoView(el, false);
17917             }
17918         }
17919     },
17920
17921     // private
17922     selectNext : function(){
17923         var ct = this.store.getCount();
17924         if(ct > 0){
17925             if(this.selectedIndex == -1){
17926                 this.select(0);
17927             }else if(this.selectedIndex < ct-1){
17928                 this.select(this.selectedIndex+1);
17929             }
17930         }
17931     },
17932
17933     // private
17934     selectPrev : function(){
17935         var ct = this.store.getCount();
17936         if(ct > 0){
17937             if(this.selectedIndex == -1){
17938                 this.select(0);
17939             }else if(this.selectedIndex != 0){
17940                 this.select(this.selectedIndex-1);
17941             }
17942         }
17943     },
17944
17945     // private
17946     onKeyUp : function(e){
17947         if(this.editable !== false && !e.isSpecialKey()){
17948             this.lastKey = e.getKey();
17949             this.dqTask.delay(this.queryDelay);
17950         }
17951     },
17952
17953     // private
17954     validateBlur : function(){
17955         return !this.list || !this.list.isVisible();   
17956     },
17957
17958     // private
17959     initQuery : function(){
17960         
17961         var v = this.getRawValue();
17962         
17963         if(this.tickable && this.editable){
17964             v = this.tickableInputEl().getValue();
17965         }
17966         
17967         this.doQuery(v);
17968     },
17969
17970     // private
17971     doForce : function(){
17972         if(this.inputEl().dom.value.length > 0){
17973             this.inputEl().dom.value =
17974                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17975              
17976         }
17977     },
17978
17979     /**
17980      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17981      * query allowing the query action to be canceled if needed.
17982      * @param {String} query The SQL query to execute
17983      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17984      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17985      * saved in the current store (defaults to false)
17986      */
17987     doQuery : function(q, forceAll){
17988         
17989         if(q === undefined || q === null){
17990             q = '';
17991         }
17992         var qe = {
17993             query: q,
17994             forceAll: forceAll,
17995             combo: this,
17996             cancel:false
17997         };
17998         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17999             return false;
18000         }
18001         q = qe.query;
18002         
18003         forceAll = qe.forceAll;
18004         if(forceAll === true || (q.length >= this.minChars)){
18005             
18006             this.hasQuery = true;
18007             
18008             if(this.lastQuery != q || this.alwaysQuery){
18009                 this.lastQuery = q;
18010                 if(this.mode == 'local'){
18011                     this.selectedIndex = -1;
18012                     if(forceAll){
18013                         this.store.clearFilter();
18014                     }else{
18015                         
18016                         if(this.specialFilter){
18017                             this.fireEvent('specialfilter', this);
18018                             this.onLoad();
18019                             return;
18020                         }
18021                         
18022                         this.store.filter(this.displayField, q);
18023                     }
18024                     
18025                     this.store.fireEvent("datachanged", this.store);
18026                     
18027                     this.onLoad();
18028                     
18029                     
18030                 }else{
18031                     
18032                     this.store.baseParams[this.queryParam] = q;
18033                     
18034                     var options = {params : this.getParams(q)};
18035                     
18036                     if(this.loadNext){
18037                         options.add = true;
18038                         options.params.start = this.page * this.pageSize;
18039                     }
18040                     
18041                     this.store.load(options);
18042                     
18043                     /*
18044                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18045                      *  we should expand the list on onLoad
18046                      *  so command out it
18047                      */
18048 //                    this.expand();
18049                 }
18050             }else{
18051                 this.selectedIndex = -1;
18052                 this.onLoad();   
18053             }
18054         }
18055         
18056         this.loadNext = false;
18057     },
18058     
18059     // private
18060     getParams : function(q){
18061         var p = {};
18062         //p[this.queryParam] = q;
18063         
18064         if(this.pageSize){
18065             p.start = 0;
18066             p.limit = this.pageSize;
18067         }
18068         return p;
18069     },
18070
18071     /**
18072      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18073      */
18074     collapse : function(){
18075         if(!this.isExpanded()){
18076             return;
18077         }
18078         
18079         this.list.hide();
18080         
18081         this.hasFocus = false;
18082         
18083         if(this.tickable){
18084             this.okBtn.hide();
18085             this.cancelBtn.hide();
18086             this.trigger.show();
18087             
18088             if(this.editable){
18089                 this.tickableInputEl().dom.value = '';
18090                 this.tickableInputEl().blur();
18091             }
18092             
18093         }
18094         
18095         Roo.get(document).un('mousedown', this.collapseIf, this);
18096         Roo.get(document).un('mousewheel', this.collapseIf, this);
18097         if (!this.editable) {
18098             Roo.get(document).un('keydown', this.listKeyPress, this);
18099         }
18100         this.fireEvent('collapse', this);
18101         
18102         this.validate();
18103     },
18104
18105     // private
18106     collapseIf : function(e){
18107         var in_combo  = e.within(this.el);
18108         var in_list =  e.within(this.list);
18109         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18110         
18111         if (in_combo || in_list || is_list) {
18112             //e.stopPropagation();
18113             return;
18114         }
18115         
18116         if(this.tickable){
18117             this.onTickableFooterButtonClick(e, false, false);
18118         }
18119
18120         this.collapse();
18121         
18122     },
18123
18124     /**
18125      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18126      */
18127     expand : function(){
18128        
18129         if(this.isExpanded() || !this.hasFocus){
18130             return;
18131         }
18132         
18133         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18134         this.list.setWidth(lw);
18135         
18136         Roo.log('expand');
18137         
18138         this.list.show();
18139         
18140         this.restrictHeight();
18141         
18142         if(this.tickable){
18143             
18144             this.tickItems = Roo.apply([], this.item);
18145             
18146             this.okBtn.show();
18147             this.cancelBtn.show();
18148             this.trigger.hide();
18149             
18150             if(this.editable){
18151                 this.tickableInputEl().focus();
18152             }
18153             
18154         }
18155         
18156         Roo.get(document).on('mousedown', this.collapseIf, this);
18157         Roo.get(document).on('mousewheel', this.collapseIf, this);
18158         if (!this.editable) {
18159             Roo.get(document).on('keydown', this.listKeyPress, this);
18160         }
18161         
18162         this.fireEvent('expand', this);
18163     },
18164
18165     // private
18166     // Implements the default empty TriggerField.onTriggerClick function
18167     onTriggerClick : function(e)
18168     {
18169         Roo.log('trigger click');
18170         
18171         if(this.disabled || !this.triggerList){
18172             return;
18173         }
18174         
18175         this.page = 0;
18176         this.loadNext = false;
18177         
18178         if(this.isExpanded()){
18179             this.collapse();
18180             if (!this.blockFocus) {
18181                 this.inputEl().focus();
18182             }
18183             
18184         }else {
18185             this.hasFocus = true;
18186             if(this.triggerAction == 'all') {
18187                 this.doQuery(this.allQuery, true);
18188             } else {
18189                 this.doQuery(this.getRawValue());
18190             }
18191             if (!this.blockFocus) {
18192                 this.inputEl().focus();
18193             }
18194         }
18195     },
18196     
18197     onTickableTriggerClick : function(e)
18198     {
18199         if(this.disabled){
18200             return;
18201         }
18202         
18203         this.page = 0;
18204         this.loadNext = false;
18205         this.hasFocus = true;
18206         
18207         if(this.triggerAction == 'all') {
18208             this.doQuery(this.allQuery, true);
18209         } else {
18210             this.doQuery(this.getRawValue());
18211         }
18212     },
18213     
18214     onSearchFieldClick : function(e)
18215     {
18216         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18217             this.onTickableFooterButtonClick(e, false, false);
18218             return;
18219         }
18220         
18221         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18222             return;
18223         }
18224         
18225         this.page = 0;
18226         this.loadNext = false;
18227         this.hasFocus = true;
18228         
18229         if(this.triggerAction == 'all') {
18230             this.doQuery(this.allQuery, true);
18231         } else {
18232             this.doQuery(this.getRawValue());
18233         }
18234     },
18235     
18236     listKeyPress : function(e)
18237     {
18238         //Roo.log('listkeypress');
18239         // scroll to first matching element based on key pres..
18240         if (e.isSpecialKey()) {
18241             return false;
18242         }
18243         var k = String.fromCharCode(e.getKey()).toUpperCase();
18244         //Roo.log(k);
18245         var match  = false;
18246         var csel = this.view.getSelectedNodes();
18247         var cselitem = false;
18248         if (csel.length) {
18249             var ix = this.view.indexOf(csel[0]);
18250             cselitem  = this.store.getAt(ix);
18251             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18252                 cselitem = false;
18253             }
18254             
18255         }
18256         
18257         this.store.each(function(v) { 
18258             if (cselitem) {
18259                 // start at existing selection.
18260                 if (cselitem.id == v.id) {
18261                     cselitem = false;
18262                 }
18263                 return true;
18264             }
18265                 
18266             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18267                 match = this.store.indexOf(v);
18268                 return false;
18269             }
18270             return true;
18271         }, this);
18272         
18273         if (match === false) {
18274             return true; // no more action?
18275         }
18276         // scroll to?
18277         this.view.select(match);
18278         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18279         sn.scrollIntoView(sn.dom.parentNode, false);
18280     },
18281     
18282     onViewScroll : function(e, t){
18283         
18284         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){
18285             return;
18286         }
18287         
18288         this.hasQuery = true;
18289         
18290         this.loading = this.list.select('.loading', true).first();
18291         
18292         if(this.loading === null){
18293             this.list.createChild({
18294                 tag: 'div',
18295                 cls: 'loading roo-select2-more-results roo-select2-active',
18296                 html: 'Loading more results...'
18297             });
18298             
18299             this.loading = this.list.select('.loading', true).first();
18300             
18301             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18302             
18303             this.loading.hide();
18304         }
18305         
18306         this.loading.show();
18307         
18308         var _combo = this;
18309         
18310         this.page++;
18311         this.loadNext = true;
18312         
18313         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18314         
18315         return;
18316     },
18317     
18318     addItem : function(o)
18319     {   
18320         var dv = ''; // display value
18321         
18322         if (this.displayField) {
18323             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18324         } else {
18325             // this is an error condition!!!
18326             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18327         }
18328         
18329         if(!dv.length){
18330             return;
18331         }
18332         
18333         var choice = this.choices.createChild({
18334             tag: 'li',
18335             cls: 'roo-select2-search-choice',
18336             cn: [
18337                 {
18338                     tag: 'div',
18339                     html: dv
18340                 },
18341                 {
18342                     tag: 'a',
18343                     href: '#',
18344                     cls: 'roo-select2-search-choice-close fa fa-times',
18345                     tabindex: '-1'
18346                 }
18347             ]
18348             
18349         }, this.searchField);
18350         
18351         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18352         
18353         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18354         
18355         this.item.push(o);
18356         
18357         this.lastData = o;
18358         
18359         this.syncValue();
18360         
18361         this.inputEl().dom.value = '';
18362         
18363         this.validate();
18364     },
18365     
18366     onRemoveItem : function(e, _self, o)
18367     {
18368         e.preventDefault();
18369         
18370         this.lastItem = Roo.apply([], this.item);
18371         
18372         var index = this.item.indexOf(o.data) * 1;
18373         
18374         if( index < 0){
18375             Roo.log('not this item?!');
18376             return;
18377         }
18378         
18379         this.item.splice(index, 1);
18380         o.item.remove();
18381         
18382         this.syncValue();
18383         
18384         this.fireEvent('remove', this, e);
18385         
18386         this.validate();
18387         
18388     },
18389     
18390     syncValue : function()
18391     {
18392         if(!this.item.length){
18393             this.clearValue();
18394             return;
18395         }
18396             
18397         var value = [];
18398         var _this = this;
18399         Roo.each(this.item, function(i){
18400             if(_this.valueField){
18401                 value.push(i[_this.valueField]);
18402                 return;
18403             }
18404
18405             value.push(i);
18406         });
18407
18408         this.value = value.join(',');
18409
18410         if(this.hiddenField){
18411             this.hiddenField.dom.value = this.value;
18412         }
18413         
18414         this.store.fireEvent("datachanged", this.store);
18415         
18416         this.validate();
18417     },
18418     
18419     clearItem : function()
18420     {
18421         if(!this.multiple){
18422             return;
18423         }
18424         
18425         this.item = [];
18426         
18427         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18428            c.remove();
18429         });
18430         
18431         this.syncValue();
18432         
18433         this.validate();
18434         
18435         if(this.tickable && !Roo.isTouch){
18436             this.view.refresh();
18437         }
18438     },
18439     
18440     inputEl: function ()
18441     {
18442         if(Roo.isIOS && this.useNativeIOS){
18443             return this.el.select('select.roo-ios-select', true).first();
18444         }
18445         
18446         if(Roo.isTouch && this.mobileTouchView){
18447             return this.el.select('input.form-control',true).first();
18448         }
18449         
18450         if(this.tickable){
18451             return this.searchField;
18452         }
18453         
18454         return this.el.select('input.form-control',true).first();
18455     },
18456     
18457     onTickableFooterButtonClick : function(e, btn, el)
18458     {
18459         e.preventDefault();
18460         
18461         this.lastItem = Roo.apply([], this.item);
18462         
18463         if(btn && btn.name == 'cancel'){
18464             this.tickItems = Roo.apply([], this.item);
18465             this.collapse();
18466             return;
18467         }
18468         
18469         this.clearItem();
18470         
18471         var _this = this;
18472         
18473         Roo.each(this.tickItems, function(o){
18474             _this.addItem(o);
18475         });
18476         
18477         this.collapse();
18478         
18479     },
18480     
18481     validate : function()
18482     {
18483         if(this.getVisibilityEl().hasClass('hidden')){
18484             return true;
18485         }
18486         
18487         var v = this.getRawValue();
18488         
18489         if(this.multiple){
18490             v = this.getValue();
18491         }
18492         
18493         if(this.disabled || this.allowBlank || v.length){
18494             this.markValid();
18495             return true;
18496         }
18497         
18498         this.markInvalid();
18499         return false;
18500     },
18501     
18502     tickableInputEl : function()
18503     {
18504         if(!this.tickable || !this.editable){
18505             return this.inputEl();
18506         }
18507         
18508         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18509     },
18510     
18511     
18512     getAutoCreateTouchView : function()
18513     {
18514         var id = Roo.id();
18515         
18516         var cfg = {
18517             cls: 'form-group' //input-group
18518         };
18519         
18520         var input =  {
18521             tag: 'input',
18522             id : id,
18523             type : this.inputType,
18524             cls : 'form-control x-combo-noedit',
18525             autocomplete: 'new-password',
18526             placeholder : this.placeholder || '',
18527             readonly : true
18528         };
18529         
18530         if (this.name) {
18531             input.name = this.name;
18532         }
18533         
18534         if (this.size) {
18535             input.cls += ' input-' + this.size;
18536         }
18537         
18538         if (this.disabled) {
18539             input.disabled = true;
18540         }
18541         
18542         var inputblock = {
18543             cls : 'roo-combobox-wrap',
18544             cn : [
18545                 input
18546             ]
18547         };
18548         
18549         if(this.before){
18550             inputblock.cls += ' input-group';
18551             
18552             inputblock.cn.unshift({
18553                 tag :'span',
18554                 cls : 'input-group-addon input-group-prepend input-group-text',
18555                 html : this.before
18556             });
18557         }
18558         
18559         if(this.removable && !this.multiple){
18560             inputblock.cls += ' roo-removable';
18561             
18562             inputblock.cn.push({
18563                 tag: 'button',
18564                 html : 'x',
18565                 cls : 'roo-combo-removable-btn close'
18566             });
18567         }
18568
18569         if(this.hasFeedback && !this.allowBlank){
18570             
18571             inputblock.cls += ' has-feedback';
18572             
18573             inputblock.cn.push({
18574                 tag: 'span',
18575                 cls: 'glyphicon form-control-feedback'
18576             });
18577             
18578         }
18579         
18580         if (this.after) {
18581             
18582             inputblock.cls += (this.before) ? '' : ' input-group';
18583             
18584             inputblock.cn.push({
18585                 tag :'span',
18586                 cls : 'input-group-addon input-group-append input-group-text',
18587                 html : this.after
18588             });
18589         }
18590
18591         
18592         var ibwrap = inputblock;
18593         
18594         if(this.multiple){
18595             ibwrap = {
18596                 tag: 'ul',
18597                 cls: 'roo-select2-choices',
18598                 cn:[
18599                     {
18600                         tag: 'li',
18601                         cls: 'roo-select2-search-field',
18602                         cn: [
18603
18604                             inputblock
18605                         ]
18606                     }
18607                 ]
18608             };
18609         
18610             
18611         }
18612         
18613         var combobox = {
18614             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18615             cn: [
18616                 {
18617                     tag: 'input',
18618                     type : 'hidden',
18619                     cls: 'form-hidden-field'
18620                 },
18621                 ibwrap
18622             ]
18623         };
18624         
18625         if(!this.multiple && this.showToggleBtn){
18626             
18627             var caret = {
18628                 cls: 'caret'
18629             };
18630             
18631             if (this.caret != false) {
18632                 caret = {
18633                      tag: 'i',
18634                      cls: 'fa fa-' + this.caret
18635                 };
18636                 
18637             }
18638             
18639             combobox.cn.push({
18640                 tag :'span',
18641                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18642                 cn : [
18643                     Roo.bootstrap.version == 3 ? caret : '',
18644                     {
18645                         tag: 'span',
18646                         cls: 'combobox-clear',
18647                         cn  : [
18648                             {
18649                                 tag : 'i',
18650                                 cls: 'icon-remove'
18651                             }
18652                         ]
18653                     }
18654                 ]
18655
18656             })
18657         }
18658         
18659         if(this.multiple){
18660             combobox.cls += ' roo-select2-container-multi';
18661         }
18662         
18663         var required =  this.allowBlank ?  {
18664                     tag : 'i',
18665                     style: 'display: none'
18666                 } : {
18667                    tag : 'i',
18668                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18669                    tooltip : 'This field is required'
18670                 };
18671         
18672         var align = this.labelAlign || this.parentLabelAlign();
18673         
18674         if (align ==='left' && this.fieldLabel.length) {
18675
18676             cfg.cn = [
18677                 required,
18678                 {
18679                     tag: 'label',
18680                     cls : 'control-label col-form-label',
18681                     html : this.fieldLabel
18682
18683                 },
18684                 {
18685                     cls : 'roo-combobox-wrap ', 
18686                     cn: [
18687                         combobox
18688                     ]
18689                 }
18690             ];
18691             
18692             var labelCfg = cfg.cn[1];
18693             var contentCfg = cfg.cn[2];
18694             
18695
18696             if(this.indicatorpos == 'right'){
18697                 cfg.cn = [
18698                     {
18699                         tag: 'label',
18700                         'for' :  id,
18701                         cls : 'control-label col-form-label',
18702                         cn : [
18703                             {
18704                                 tag : 'span',
18705                                 html : this.fieldLabel
18706                             },
18707                             required
18708                         ]
18709                     },
18710                     {
18711                         cls : "roo-combobox-wrap ",
18712                         cn: [
18713                             combobox
18714                         ]
18715                     }
18716
18717                 ];
18718                 
18719                 labelCfg = cfg.cn[0];
18720                 contentCfg = cfg.cn[1];
18721             }
18722             
18723            
18724             
18725             if(this.labelWidth > 12){
18726                 labelCfg.style = "width: " + this.labelWidth + 'px';
18727             }
18728            
18729             if(this.labelWidth < 13 && this.labelmd == 0){
18730                 this.labelmd = this.labelWidth;
18731             }
18732             
18733             if(this.labellg > 0){
18734                 labelCfg.cls += ' col-lg-' + this.labellg;
18735                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18736             }
18737             
18738             if(this.labelmd > 0){
18739                 labelCfg.cls += ' col-md-' + this.labelmd;
18740                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18741             }
18742             
18743             if(this.labelsm > 0){
18744                 labelCfg.cls += ' col-sm-' + this.labelsm;
18745                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18746             }
18747             
18748             if(this.labelxs > 0){
18749                 labelCfg.cls += ' col-xs-' + this.labelxs;
18750                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18751             }
18752                 
18753                 
18754         } else if ( this.fieldLabel.length) {
18755             cfg.cn = [
18756                required,
18757                 {
18758                     tag: 'label',
18759                     cls : 'control-label',
18760                     html : this.fieldLabel
18761
18762                 },
18763                 {
18764                     cls : '', 
18765                     cn: [
18766                         combobox
18767                     ]
18768                 }
18769             ];
18770             
18771             if(this.indicatorpos == 'right'){
18772                 cfg.cn = [
18773                     {
18774                         tag: 'label',
18775                         cls : 'control-label',
18776                         html : this.fieldLabel,
18777                         cn : [
18778                             required
18779                         ]
18780                     },
18781                     {
18782                         cls : '', 
18783                         cn: [
18784                             combobox
18785                         ]
18786                     }
18787                 ];
18788             }
18789         } else {
18790             cfg.cn = combobox;    
18791         }
18792         
18793         
18794         var settings = this;
18795         
18796         ['xs','sm','md','lg'].map(function(size){
18797             if (settings[size]) {
18798                 cfg.cls += ' col-' + size + '-' + settings[size];
18799             }
18800         });
18801         
18802         return cfg;
18803     },
18804     
18805     initTouchView : function()
18806     {
18807         this.renderTouchView();
18808         
18809         this.touchViewEl.on('scroll', function(){
18810             this.el.dom.scrollTop = 0;
18811         }, this);
18812         
18813         this.originalValue = this.getValue();
18814         
18815         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18816         
18817         this.inputEl().on("click", this.showTouchView, this);
18818         if (this.triggerEl) {
18819             this.triggerEl.on("click", this.showTouchView, this);
18820         }
18821         
18822         
18823         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18824         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18825         
18826         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18827         
18828         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18829         this.store.on('load', this.onTouchViewLoad, this);
18830         this.store.on('loadexception', this.onTouchViewLoadException, this);
18831         
18832         if(this.hiddenName){
18833             
18834             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18835             
18836             this.hiddenField.dom.value =
18837                 this.hiddenValue !== undefined ? this.hiddenValue :
18838                 this.value !== undefined ? this.value : '';
18839         
18840             this.el.dom.removeAttribute('name');
18841             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18842         }
18843         
18844         if(this.multiple){
18845             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18846             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18847         }
18848         
18849         if(this.removable && !this.multiple){
18850             var close = this.closeTriggerEl();
18851             if(close){
18852                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18853                 close.on('click', this.removeBtnClick, this, close);
18854             }
18855         }
18856         /*
18857          * fix the bug in Safari iOS8
18858          */
18859         this.inputEl().on("focus", function(e){
18860             document.activeElement.blur();
18861         }, this);
18862         
18863         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18864         
18865         return;
18866         
18867         
18868     },
18869     
18870     renderTouchView : function()
18871     {
18872         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18873         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18874         
18875         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18876         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18877         
18878         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18879         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         this.touchViewBodyEl.setStyle('overflow', 'auto');
18881         
18882         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18883         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884         
18885         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18886         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         
18888     },
18889     
18890     showTouchView : function()
18891     {
18892         if(this.disabled){
18893             return;
18894         }
18895         
18896         this.touchViewHeaderEl.hide();
18897
18898         if(this.modalTitle.length){
18899             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18900             this.touchViewHeaderEl.show();
18901         }
18902
18903         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18904         this.touchViewEl.show();
18905
18906         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18907         
18908         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18909         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18910
18911         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18912
18913         if(this.modalTitle.length){
18914             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18915         }
18916         
18917         this.touchViewBodyEl.setHeight(bodyHeight);
18918
18919         if(this.animate){
18920             var _this = this;
18921             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18922         }else{
18923             this.touchViewEl.addClass(['in','show']);
18924         }
18925         
18926         if(this._touchViewMask){
18927             Roo.get(document.body).addClass("x-body-masked");
18928             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18929             this._touchViewMask.setStyle('z-index', 10000);
18930             this._touchViewMask.addClass('show');
18931         }
18932         
18933         this.doTouchViewQuery();
18934         
18935     },
18936     
18937     hideTouchView : function()
18938     {
18939         this.touchViewEl.removeClass(['in','show']);
18940
18941         if(this.animate){
18942             var _this = this;
18943             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18944         }else{
18945             this.touchViewEl.setStyle('display', 'none');
18946         }
18947         
18948         if(this._touchViewMask){
18949             this._touchViewMask.removeClass('show');
18950             Roo.get(document.body).removeClass("x-body-masked");
18951         }
18952     },
18953     
18954     setTouchViewValue : function()
18955     {
18956         if(this.multiple){
18957             this.clearItem();
18958         
18959             var _this = this;
18960
18961             Roo.each(this.tickItems, function(o){
18962                 this.addItem(o);
18963             }, this);
18964         }
18965         
18966         this.hideTouchView();
18967     },
18968     
18969     doTouchViewQuery : function()
18970     {
18971         var qe = {
18972             query: '',
18973             forceAll: true,
18974             combo: this,
18975             cancel:false
18976         };
18977         
18978         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18979             return false;
18980         }
18981         
18982         if(!this.alwaysQuery || this.mode == 'local'){
18983             this.onTouchViewLoad();
18984             return;
18985         }
18986         
18987         this.store.load();
18988     },
18989     
18990     onTouchViewBeforeLoad : function(combo,opts)
18991     {
18992         return;
18993     },
18994
18995     // private
18996     onTouchViewLoad : function()
18997     {
18998         if(this.store.getCount() < 1){
18999             this.onTouchViewEmptyResults();
19000             return;
19001         }
19002         
19003         this.clearTouchView();
19004         
19005         var rawValue = this.getRawValue();
19006         
19007         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19008         
19009         this.tickItems = [];
19010         
19011         this.store.data.each(function(d, rowIndex){
19012             var row = this.touchViewListGroup.createChild(template);
19013             
19014             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19015                 row.addClass(d.data.cls);
19016             }
19017             
19018             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19019                 var cfg = {
19020                     data : d.data,
19021                     html : d.data[this.displayField]
19022                 };
19023                 
19024                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19025                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19026                 }
19027             }
19028             row.removeClass('selected');
19029             if(!this.multiple && this.valueField &&
19030                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19031             {
19032                 // radio buttons..
19033                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19034                 row.addClass('selected');
19035             }
19036             
19037             if(this.multiple && this.valueField &&
19038                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19039             {
19040                 
19041                 // checkboxes...
19042                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19043                 this.tickItems.push(d.data);
19044             }
19045             
19046             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19047             
19048         }, this);
19049         
19050         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19051         
19052         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19053
19054         if(this.modalTitle.length){
19055             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19056         }
19057
19058         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19059         
19060         if(this.mobile_restrict_height && listHeight < bodyHeight){
19061             this.touchViewBodyEl.setHeight(listHeight);
19062         }
19063         
19064         var _this = this;
19065         
19066         if(firstChecked && listHeight > bodyHeight){
19067             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19068         }
19069         
19070     },
19071     
19072     onTouchViewLoadException : function()
19073     {
19074         this.hideTouchView();
19075     },
19076     
19077     onTouchViewEmptyResults : function()
19078     {
19079         this.clearTouchView();
19080         
19081         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19082         
19083         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19084         
19085     },
19086     
19087     clearTouchView : function()
19088     {
19089         this.touchViewListGroup.dom.innerHTML = '';
19090     },
19091     
19092     onTouchViewClick : function(e, el, o)
19093     {
19094         e.preventDefault();
19095         
19096         var row = o.row;
19097         var rowIndex = o.rowIndex;
19098         
19099         var r = this.store.getAt(rowIndex);
19100         
19101         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19102             
19103             if(!this.multiple){
19104                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19105                     c.dom.removeAttribute('checked');
19106                 }, this);
19107
19108                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19109
19110                 this.setFromData(r.data);
19111
19112                 var close = this.closeTriggerEl();
19113
19114                 if(close){
19115                     close.show();
19116                 }
19117
19118                 this.hideTouchView();
19119
19120                 this.fireEvent('select', this, r, rowIndex);
19121
19122                 return;
19123             }
19124
19125             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19126                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19127                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19128                 return;
19129             }
19130
19131             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19132             this.addItem(r.data);
19133             this.tickItems.push(r.data);
19134         }
19135     },
19136     
19137     getAutoCreateNativeIOS : function()
19138     {
19139         var cfg = {
19140             cls: 'form-group' //input-group,
19141         };
19142         
19143         var combobox =  {
19144             tag: 'select',
19145             cls : 'roo-ios-select'
19146         };
19147         
19148         if (this.name) {
19149             combobox.name = this.name;
19150         }
19151         
19152         if (this.disabled) {
19153             combobox.disabled = true;
19154         }
19155         
19156         var settings = this;
19157         
19158         ['xs','sm','md','lg'].map(function(size){
19159             if (settings[size]) {
19160                 cfg.cls += ' col-' + size + '-' + settings[size];
19161             }
19162         });
19163         
19164         cfg.cn = combobox;
19165         
19166         return cfg;
19167         
19168     },
19169     
19170     initIOSView : function()
19171     {
19172         this.store.on('load', this.onIOSViewLoad, this);
19173         
19174         return;
19175     },
19176     
19177     onIOSViewLoad : function()
19178     {
19179         if(this.store.getCount() < 1){
19180             return;
19181         }
19182         
19183         this.clearIOSView();
19184         
19185         if(this.allowBlank) {
19186             
19187             var default_text = '-- SELECT --';
19188             
19189             if(this.placeholder.length){
19190                 default_text = this.placeholder;
19191             }
19192             
19193             if(this.emptyTitle.length){
19194                 default_text += ' - ' + this.emptyTitle + ' -';
19195             }
19196             
19197             var opt = this.inputEl().createChild({
19198                 tag: 'option',
19199                 value : 0,
19200                 html : default_text
19201             });
19202             
19203             var o = {};
19204             o[this.valueField] = 0;
19205             o[this.displayField] = default_text;
19206             
19207             this.ios_options.push({
19208                 data : o,
19209                 el : opt
19210             });
19211             
19212         }
19213         
19214         this.store.data.each(function(d, rowIndex){
19215             
19216             var html = '';
19217             
19218             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19219                 html = d.data[this.displayField];
19220             }
19221             
19222             var value = '';
19223             
19224             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19225                 value = d.data[this.valueField];
19226             }
19227             
19228             var option = {
19229                 tag: 'option',
19230                 value : value,
19231                 html : html
19232             };
19233             
19234             if(this.value == d.data[this.valueField]){
19235                 option['selected'] = true;
19236             }
19237             
19238             var opt = this.inputEl().createChild(option);
19239             
19240             this.ios_options.push({
19241                 data : d.data,
19242                 el : opt
19243             });
19244             
19245         }, this);
19246         
19247         this.inputEl().on('change', function(){
19248            this.fireEvent('select', this);
19249         }, this);
19250         
19251     },
19252     
19253     clearIOSView: function()
19254     {
19255         this.inputEl().dom.innerHTML = '';
19256         
19257         this.ios_options = [];
19258     },
19259     
19260     setIOSValue: function(v)
19261     {
19262         this.value = v;
19263         
19264         if(!this.ios_options){
19265             return;
19266         }
19267         
19268         Roo.each(this.ios_options, function(opts){
19269            
19270            opts.el.dom.removeAttribute('selected');
19271            
19272            if(opts.data[this.valueField] != v){
19273                return;
19274            }
19275            
19276            opts.el.dom.setAttribute('selected', true);
19277            
19278         }, this);
19279     }
19280
19281     /** 
19282     * @cfg {Boolean} grow 
19283     * @hide 
19284     */
19285     /** 
19286     * @cfg {Number} growMin 
19287     * @hide 
19288     */
19289     /** 
19290     * @cfg {Number} growMax 
19291     * @hide 
19292     */
19293     /**
19294      * @hide
19295      * @method autoSize
19296      */
19297 });
19298
19299 Roo.apply(Roo.bootstrap.ComboBox,  {
19300     
19301     header : {
19302         tag: 'div',
19303         cls: 'modal-header',
19304         cn: [
19305             {
19306                 tag: 'h4',
19307                 cls: 'modal-title'
19308             }
19309         ]
19310     },
19311     
19312     body : {
19313         tag: 'div',
19314         cls: 'modal-body',
19315         cn: [
19316             {
19317                 tag: 'ul',
19318                 cls: 'list-group'
19319             }
19320         ]
19321     },
19322     
19323     listItemRadio : {
19324         tag: 'li',
19325         cls: 'list-group-item',
19326         cn: [
19327             {
19328                 tag: 'span',
19329                 cls: 'roo-combobox-list-group-item-value'
19330             },
19331             {
19332                 tag: 'div',
19333                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19334                 cn: [
19335                     {
19336                         tag: 'input',
19337                         type: 'radio'
19338                     },
19339                     {
19340                         tag: 'label'
19341                     }
19342                 ]
19343             }
19344         ]
19345     },
19346     
19347     listItemCheckbox : {
19348         tag: 'li',
19349         cls: 'list-group-item',
19350         cn: [
19351             {
19352                 tag: 'span',
19353                 cls: 'roo-combobox-list-group-item-value'
19354             },
19355             {
19356                 tag: 'div',
19357                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19358                 cn: [
19359                     {
19360                         tag: 'input',
19361                         type: 'checkbox'
19362                     },
19363                     {
19364                         tag: 'label'
19365                     }
19366                 ]
19367             }
19368         ]
19369     },
19370     
19371     emptyResult : {
19372         tag: 'div',
19373         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19374     },
19375     
19376     footer : {
19377         tag: 'div',
19378         cls: 'modal-footer',
19379         cn: [
19380             {
19381                 tag: 'div',
19382                 cls: 'row',
19383                 cn: [
19384                     {
19385                         tag: 'div',
19386                         cls: 'col-xs-6 text-left',
19387                         cn: {
19388                             tag: 'button',
19389                             cls: 'btn btn-danger roo-touch-view-cancel',
19390                             html: 'Cancel'
19391                         }
19392                     },
19393                     {
19394                         tag: 'div',
19395                         cls: 'col-xs-6 text-right',
19396                         cn: {
19397                             tag: 'button',
19398                             cls: 'btn btn-success roo-touch-view-ok',
19399                             html: 'OK'
19400                         }
19401                     }
19402                 ]
19403             }
19404         ]
19405         
19406     }
19407 });
19408
19409 Roo.apply(Roo.bootstrap.ComboBox,  {
19410     
19411     touchViewTemplate : {
19412         tag: 'div',
19413         cls: 'modal fade roo-combobox-touch-view',
19414         cn: [
19415             {
19416                 tag: 'div',
19417                 cls: 'modal-dialog',
19418                 style : 'position:fixed', // we have to fix position....
19419                 cn: [
19420                     {
19421                         tag: 'div',
19422                         cls: 'modal-content',
19423                         cn: [
19424                             Roo.bootstrap.ComboBox.header,
19425                             Roo.bootstrap.ComboBox.body,
19426                             Roo.bootstrap.ComboBox.footer
19427                         ]
19428                     }
19429                 ]
19430             }
19431         ]
19432     }
19433 });/*
19434  * Based on:
19435  * Ext JS Library 1.1.1
19436  * Copyright(c) 2006-2007, Ext JS, LLC.
19437  *
19438  * Originally Released Under LGPL - original licence link has changed is not relivant.
19439  *
19440  * Fork - LGPL
19441  * <script type="text/javascript">
19442  */
19443
19444 /**
19445  * @class Roo.View
19446  * @extends Roo.util.Observable
19447  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19448  * This class also supports single and multi selection modes. <br>
19449  * Create a data model bound view:
19450  <pre><code>
19451  var store = new Roo.data.Store(...);
19452
19453  var view = new Roo.View({
19454     el : "my-element",
19455     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19456  
19457     singleSelect: true,
19458     selectedClass: "ydataview-selected",
19459     store: store
19460  });
19461
19462  // listen for node click?
19463  view.on("click", function(vw, index, node, e){
19464  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19465  });
19466
19467  // load XML data
19468  dataModel.load("foobar.xml");
19469  </code></pre>
19470  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19471  * <br><br>
19472  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19473  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19474  * 
19475  * Note: old style constructor is still suported (container, template, config)
19476  * 
19477  * @constructor
19478  * Create a new View
19479  * @param {Object} config The config object
19480  * 
19481  */
19482 Roo.View = function(config, depreciated_tpl, depreciated_config){
19483     
19484     this.parent = false;
19485     
19486     if (typeof(depreciated_tpl) == 'undefined') {
19487         // new way.. - universal constructor.
19488         Roo.apply(this, config);
19489         this.el  = Roo.get(this.el);
19490     } else {
19491         // old format..
19492         this.el  = Roo.get(config);
19493         this.tpl = depreciated_tpl;
19494         Roo.apply(this, depreciated_config);
19495     }
19496     this.wrapEl  = this.el.wrap().wrap();
19497     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19498     
19499     
19500     if(typeof(this.tpl) == "string"){
19501         this.tpl = new Roo.Template(this.tpl);
19502     } else {
19503         // support xtype ctors..
19504         this.tpl = new Roo.factory(this.tpl, Roo);
19505     }
19506     
19507     
19508     this.tpl.compile();
19509     
19510     /** @private */
19511     this.addEvents({
19512         /**
19513          * @event beforeclick
19514          * Fires before a click is processed. Returns false to cancel the default action.
19515          * @param {Roo.View} this
19516          * @param {Number} index The index of the target node
19517          * @param {HTMLElement} node The target node
19518          * @param {Roo.EventObject} e The raw event object
19519          */
19520             "beforeclick" : true,
19521         /**
19522          * @event click
19523          * Fires when a template node is clicked.
19524          * @param {Roo.View} this
19525          * @param {Number} index The index of the target node
19526          * @param {HTMLElement} node The target node
19527          * @param {Roo.EventObject} e The raw event object
19528          */
19529             "click" : true,
19530         /**
19531          * @event dblclick
19532          * Fires when a template node is double clicked.
19533          * @param {Roo.View} this
19534          * @param {Number} index The index of the target node
19535          * @param {HTMLElement} node The target node
19536          * @param {Roo.EventObject} e The raw event object
19537          */
19538             "dblclick" : true,
19539         /**
19540          * @event contextmenu
19541          * Fires when a template node is right clicked.
19542          * @param {Roo.View} this
19543          * @param {Number} index The index of the target node
19544          * @param {HTMLElement} node The target node
19545          * @param {Roo.EventObject} e The raw event object
19546          */
19547             "contextmenu" : true,
19548         /**
19549          * @event selectionchange
19550          * Fires when the selected nodes change.
19551          * @param {Roo.View} this
19552          * @param {Array} selections Array of the selected nodes
19553          */
19554             "selectionchange" : true,
19555     
19556         /**
19557          * @event beforeselect
19558          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19559          * @param {Roo.View} this
19560          * @param {HTMLElement} node The node to be selected
19561          * @param {Array} selections Array of currently selected nodes
19562          */
19563             "beforeselect" : true,
19564         /**
19565          * @event preparedata
19566          * Fires on every row to render, to allow you to change the data.
19567          * @param {Roo.View} this
19568          * @param {Object} data to be rendered (change this)
19569          */
19570           "preparedata" : true
19571           
19572           
19573         });
19574
19575
19576
19577     this.el.on({
19578         "click": this.onClick,
19579         "dblclick": this.onDblClick,
19580         "contextmenu": this.onContextMenu,
19581         scope:this
19582     });
19583
19584     this.selections = [];
19585     this.nodes = [];
19586     this.cmp = new Roo.CompositeElementLite([]);
19587     if(this.store){
19588         this.store = Roo.factory(this.store, Roo.data);
19589         this.setStore(this.store, true);
19590     }
19591     
19592     if ( this.footer && this.footer.xtype) {
19593            
19594          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19595         
19596         this.footer.dataSource = this.store;
19597         this.footer.container = fctr;
19598         this.footer = Roo.factory(this.footer, Roo);
19599         fctr.insertFirst(this.el);
19600         
19601         // this is a bit insane - as the paging toolbar seems to detach the el..
19602 //        dom.parentNode.parentNode.parentNode
19603          // they get detached?
19604     }
19605     
19606     
19607     Roo.View.superclass.constructor.call(this);
19608     
19609     
19610 };
19611
19612 Roo.extend(Roo.View, Roo.util.Observable, {
19613     
19614      /**
19615      * @cfg {Roo.data.Store} store Data store to load data from.
19616      */
19617     store : false,
19618     
19619     /**
19620      * @cfg {String|Roo.Element} el The container element.
19621      */
19622     el : '',
19623     
19624     /**
19625      * @cfg {String|Roo.Template} tpl The template used by this View 
19626      */
19627     tpl : false,
19628     /**
19629      * @cfg {String} dataName the named area of the template to use as the data area
19630      *                          Works with domtemplates roo-name="name"
19631      */
19632     dataName: false,
19633     /**
19634      * @cfg {String} selectedClass The css class to add to selected nodes
19635      */
19636     selectedClass : "x-view-selected",
19637      /**
19638      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19639      */
19640     emptyText : "",
19641     
19642     /**
19643      * @cfg {String} text to display on mask (default Loading)
19644      */
19645     mask : false,
19646     /**
19647      * @cfg {Boolean} multiSelect Allow multiple selection
19648      */
19649     multiSelect : false,
19650     /**
19651      * @cfg {Boolean} singleSelect Allow single selection
19652      */
19653     singleSelect:  false,
19654     
19655     /**
19656      * @cfg {Boolean} toggleSelect - selecting 
19657      */
19658     toggleSelect : false,
19659     
19660     /**
19661      * @cfg {Boolean} tickable - selecting 
19662      */
19663     tickable : false,
19664     
19665     /**
19666      * Returns the element this view is bound to.
19667      * @return {Roo.Element}
19668      */
19669     getEl : function(){
19670         return this.wrapEl;
19671     },
19672     
19673     
19674
19675     /**
19676      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19677      */
19678     refresh : function(){
19679         //Roo.log('refresh');
19680         var t = this.tpl;
19681         
19682         // if we are using something like 'domtemplate', then
19683         // the what gets used is:
19684         // t.applySubtemplate(NAME, data, wrapping data..)
19685         // the outer template then get' applied with
19686         //     the store 'extra data'
19687         // and the body get's added to the
19688         //      roo-name="data" node?
19689         //      <span class='roo-tpl-{name}'></span> ?????
19690         
19691         
19692         
19693         this.clearSelections();
19694         this.el.update("");
19695         var html = [];
19696         var records = this.store.getRange();
19697         if(records.length < 1) {
19698             
19699             // is this valid??  = should it render a template??
19700             
19701             this.el.update(this.emptyText);
19702             return;
19703         }
19704         var el = this.el;
19705         if (this.dataName) {
19706             this.el.update(t.apply(this.store.meta)); //????
19707             el = this.el.child('.roo-tpl-' + this.dataName);
19708         }
19709         
19710         for(var i = 0, len = records.length; i < len; i++){
19711             var data = this.prepareData(records[i].data, i, records[i]);
19712             this.fireEvent("preparedata", this, data, i, records[i]);
19713             
19714             var d = Roo.apply({}, data);
19715             
19716             if(this.tickable){
19717                 Roo.apply(d, {'roo-id' : Roo.id()});
19718                 
19719                 var _this = this;
19720             
19721                 Roo.each(this.parent.item, function(item){
19722                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19723                         return;
19724                     }
19725                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19726                 });
19727             }
19728             
19729             html[html.length] = Roo.util.Format.trim(
19730                 this.dataName ?
19731                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19732                     t.apply(d)
19733             );
19734         }
19735         
19736         
19737         
19738         el.update(html.join(""));
19739         this.nodes = el.dom.childNodes;
19740         this.updateIndexes(0);
19741     },
19742     
19743
19744     /**
19745      * Function to override to reformat the data that is sent to
19746      * the template for each node.
19747      * DEPRICATED - use the preparedata event handler.
19748      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19749      * a JSON object for an UpdateManager bound view).
19750      */
19751     prepareData : function(data, index, record)
19752     {
19753         this.fireEvent("preparedata", this, data, index, record);
19754         return data;
19755     },
19756
19757     onUpdate : function(ds, record){
19758         // Roo.log('on update');   
19759         this.clearSelections();
19760         var index = this.store.indexOf(record);
19761         var n = this.nodes[index];
19762         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19763         n.parentNode.removeChild(n);
19764         this.updateIndexes(index, index);
19765     },
19766
19767     
19768     
19769 // --------- FIXME     
19770     onAdd : function(ds, records, index)
19771     {
19772         //Roo.log(['on Add', ds, records, index] );        
19773         this.clearSelections();
19774         if(this.nodes.length == 0){
19775             this.refresh();
19776             return;
19777         }
19778         var n = this.nodes[index];
19779         for(var i = 0, len = records.length; i < len; i++){
19780             var d = this.prepareData(records[i].data, i, records[i]);
19781             if(n){
19782                 this.tpl.insertBefore(n, d);
19783             }else{
19784                 
19785                 this.tpl.append(this.el, d);
19786             }
19787         }
19788         this.updateIndexes(index);
19789     },
19790
19791     onRemove : function(ds, record, index){
19792        // Roo.log('onRemove');
19793         this.clearSelections();
19794         var el = this.dataName  ?
19795             this.el.child('.roo-tpl-' + this.dataName) :
19796             this.el; 
19797         
19798         el.dom.removeChild(this.nodes[index]);
19799         this.updateIndexes(index);
19800     },
19801
19802     /**
19803      * Refresh an individual node.
19804      * @param {Number} index
19805      */
19806     refreshNode : function(index){
19807         this.onUpdate(this.store, this.store.getAt(index));
19808     },
19809
19810     updateIndexes : function(startIndex, endIndex){
19811         var ns = this.nodes;
19812         startIndex = startIndex || 0;
19813         endIndex = endIndex || ns.length - 1;
19814         for(var i = startIndex; i <= endIndex; i++){
19815             ns[i].nodeIndex = i;
19816         }
19817     },
19818
19819     /**
19820      * Changes the data store this view uses and refresh the view.
19821      * @param {Store} store
19822      */
19823     setStore : function(store, initial){
19824         if(!initial && this.store){
19825             this.store.un("datachanged", this.refresh);
19826             this.store.un("add", this.onAdd);
19827             this.store.un("remove", this.onRemove);
19828             this.store.un("update", this.onUpdate);
19829             this.store.un("clear", this.refresh);
19830             this.store.un("beforeload", this.onBeforeLoad);
19831             this.store.un("load", this.onLoad);
19832             this.store.un("loadexception", this.onLoad);
19833         }
19834         if(store){
19835           
19836             store.on("datachanged", this.refresh, this);
19837             store.on("add", this.onAdd, this);
19838             store.on("remove", this.onRemove, this);
19839             store.on("update", this.onUpdate, this);
19840             store.on("clear", this.refresh, this);
19841             store.on("beforeload", this.onBeforeLoad, this);
19842             store.on("load", this.onLoad, this);
19843             store.on("loadexception", this.onLoad, this);
19844         }
19845         
19846         if(store){
19847             this.refresh();
19848         }
19849     },
19850     /**
19851      * onbeforeLoad - masks the loading area.
19852      *
19853      */
19854     onBeforeLoad : function(store,opts)
19855     {
19856          //Roo.log('onBeforeLoad');   
19857         if (!opts.add) {
19858             this.el.update("");
19859         }
19860         this.el.mask(this.mask ? this.mask : "Loading" ); 
19861     },
19862     onLoad : function ()
19863     {
19864         this.el.unmask();
19865     },
19866     
19867
19868     /**
19869      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19870      * @param {HTMLElement} node
19871      * @return {HTMLElement} The template node
19872      */
19873     findItemFromChild : function(node){
19874         var el = this.dataName  ?
19875             this.el.child('.roo-tpl-' + this.dataName,true) :
19876             this.el.dom; 
19877         
19878         if(!node || node.parentNode == el){
19879                     return node;
19880             }
19881             var p = node.parentNode;
19882             while(p && p != el){
19883             if(p.parentNode == el){
19884                 return p;
19885             }
19886             p = p.parentNode;
19887         }
19888             return null;
19889     },
19890
19891     /** @ignore */
19892     onClick : function(e){
19893         var item = this.findItemFromChild(e.getTarget());
19894         if(item){
19895             var index = this.indexOf(item);
19896             if(this.onItemClick(item, index, e) !== false){
19897                 this.fireEvent("click", this, index, item, e);
19898             }
19899         }else{
19900             this.clearSelections();
19901         }
19902     },
19903
19904     /** @ignore */
19905     onContextMenu : function(e){
19906         var item = this.findItemFromChild(e.getTarget());
19907         if(item){
19908             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19909         }
19910     },
19911
19912     /** @ignore */
19913     onDblClick : function(e){
19914         var item = this.findItemFromChild(e.getTarget());
19915         if(item){
19916             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19917         }
19918     },
19919
19920     onItemClick : function(item, index, e)
19921     {
19922         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19923             return false;
19924         }
19925         if (this.toggleSelect) {
19926             var m = this.isSelected(item) ? 'unselect' : 'select';
19927             //Roo.log(m);
19928             var _t = this;
19929             _t[m](item, true, false);
19930             return true;
19931         }
19932         if(this.multiSelect || this.singleSelect){
19933             if(this.multiSelect && e.shiftKey && this.lastSelection){
19934                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19935             }else{
19936                 this.select(item, this.multiSelect && e.ctrlKey);
19937                 this.lastSelection = item;
19938             }
19939             
19940             if(!this.tickable){
19941                 e.preventDefault();
19942             }
19943             
19944         }
19945         return true;
19946     },
19947
19948     /**
19949      * Get the number of selected nodes.
19950      * @return {Number}
19951      */
19952     getSelectionCount : function(){
19953         return this.selections.length;
19954     },
19955
19956     /**
19957      * Get the currently selected nodes.
19958      * @return {Array} An array of HTMLElements
19959      */
19960     getSelectedNodes : function(){
19961         return this.selections;
19962     },
19963
19964     /**
19965      * Get the indexes of the selected nodes.
19966      * @return {Array}
19967      */
19968     getSelectedIndexes : function(){
19969         var indexes = [], s = this.selections;
19970         for(var i = 0, len = s.length; i < len; i++){
19971             indexes.push(s[i].nodeIndex);
19972         }
19973         return indexes;
19974     },
19975
19976     /**
19977      * Clear all selections
19978      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19979      */
19980     clearSelections : function(suppressEvent){
19981         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19982             this.cmp.elements = this.selections;
19983             this.cmp.removeClass(this.selectedClass);
19984             this.selections = [];
19985             if(!suppressEvent){
19986                 this.fireEvent("selectionchange", this, this.selections);
19987             }
19988         }
19989     },
19990
19991     /**
19992      * Returns true if the passed node is selected
19993      * @param {HTMLElement/Number} node The node or node index
19994      * @return {Boolean}
19995      */
19996     isSelected : function(node){
19997         var s = this.selections;
19998         if(s.length < 1){
19999             return false;
20000         }
20001         node = this.getNode(node);
20002         return s.indexOf(node) !== -1;
20003     },
20004
20005     /**
20006      * Selects nodes.
20007      * @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
20008      * @param {Boolean} keepExisting (optional) true to keep existing selections
20009      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20010      */
20011     select : function(nodeInfo, keepExisting, suppressEvent){
20012         if(nodeInfo instanceof Array){
20013             if(!keepExisting){
20014                 this.clearSelections(true);
20015             }
20016             for(var i = 0, len = nodeInfo.length; i < len; i++){
20017                 this.select(nodeInfo[i], true, true);
20018             }
20019             return;
20020         } 
20021         var node = this.getNode(nodeInfo);
20022         if(!node || this.isSelected(node)){
20023             return; // already selected.
20024         }
20025         if(!keepExisting){
20026             this.clearSelections(true);
20027         }
20028         
20029         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20030             Roo.fly(node).addClass(this.selectedClass);
20031             this.selections.push(node);
20032             if(!suppressEvent){
20033                 this.fireEvent("selectionchange", this, this.selections);
20034             }
20035         }
20036         
20037         
20038     },
20039       /**
20040      * Unselects nodes.
20041      * @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
20042      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20043      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20044      */
20045     unselect : function(nodeInfo, keepExisting, suppressEvent)
20046     {
20047         if(nodeInfo instanceof Array){
20048             Roo.each(this.selections, function(s) {
20049                 this.unselect(s, nodeInfo);
20050             }, this);
20051             return;
20052         }
20053         var node = this.getNode(nodeInfo);
20054         if(!node || !this.isSelected(node)){
20055             //Roo.log("not selected");
20056             return; // not selected.
20057         }
20058         // fireevent???
20059         var ns = [];
20060         Roo.each(this.selections, function(s) {
20061             if (s == node ) {
20062                 Roo.fly(node).removeClass(this.selectedClass);
20063
20064                 return;
20065             }
20066             ns.push(s);
20067         },this);
20068         
20069         this.selections= ns;
20070         this.fireEvent("selectionchange", this, this.selections);
20071     },
20072
20073     /**
20074      * Gets a template node.
20075      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20076      * @return {HTMLElement} The node or null if it wasn't found
20077      */
20078     getNode : function(nodeInfo){
20079         if(typeof nodeInfo == "string"){
20080             return document.getElementById(nodeInfo);
20081         }else if(typeof nodeInfo == "number"){
20082             return this.nodes[nodeInfo];
20083         }
20084         return nodeInfo;
20085     },
20086
20087     /**
20088      * Gets a range template nodes.
20089      * @param {Number} startIndex
20090      * @param {Number} endIndex
20091      * @return {Array} An array of nodes
20092      */
20093     getNodes : function(start, end){
20094         var ns = this.nodes;
20095         start = start || 0;
20096         end = typeof end == "undefined" ? ns.length - 1 : end;
20097         var nodes = [];
20098         if(start <= end){
20099             for(var i = start; i <= end; i++){
20100                 nodes.push(ns[i]);
20101             }
20102         } else{
20103             for(var i = start; i >= end; i--){
20104                 nodes.push(ns[i]);
20105             }
20106         }
20107         return nodes;
20108     },
20109
20110     /**
20111      * Finds the index of the passed node
20112      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20113      * @return {Number} The index of the node or -1
20114      */
20115     indexOf : function(node){
20116         node = this.getNode(node);
20117         if(typeof node.nodeIndex == "number"){
20118             return node.nodeIndex;
20119         }
20120         var ns = this.nodes;
20121         for(var i = 0, len = ns.length; i < len; i++){
20122             if(ns[i] == node){
20123                 return i;
20124             }
20125         }
20126         return -1;
20127     }
20128 });
20129 /*
20130  * - LGPL
20131  *
20132  * based on jquery fullcalendar
20133  * 
20134  */
20135
20136 Roo.bootstrap = Roo.bootstrap || {};
20137 /**
20138  * @class Roo.bootstrap.Calendar
20139  * @extends Roo.bootstrap.Component
20140  * Bootstrap Calendar class
20141  * @cfg {Boolean} loadMask (true|false) default false
20142  * @cfg {Object} header generate the user specific header of the calendar, default false
20143
20144  * @constructor
20145  * Create a new Container
20146  * @param {Object} config The config object
20147  */
20148
20149
20150
20151 Roo.bootstrap.Calendar = function(config){
20152     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20153      this.addEvents({
20154         /**
20155              * @event select
20156              * Fires when a date is selected
20157              * @param {DatePicker} this
20158              * @param {Date} date The selected date
20159              */
20160         'select': true,
20161         /**
20162              * @event monthchange
20163              * Fires when the displayed month changes 
20164              * @param {DatePicker} this
20165              * @param {Date} date The selected month
20166              */
20167         'monthchange': true,
20168         /**
20169              * @event evententer
20170              * Fires when mouse over an event
20171              * @param {Calendar} this
20172              * @param {event} Event
20173              */
20174         'evententer': true,
20175         /**
20176              * @event eventleave
20177              * Fires when the mouse leaves an
20178              * @param {Calendar} this
20179              * @param {event}
20180              */
20181         'eventleave': true,
20182         /**
20183              * @event eventclick
20184              * Fires when the mouse click an
20185              * @param {Calendar} this
20186              * @param {event}
20187              */
20188         'eventclick': true
20189         
20190     });
20191
20192 };
20193
20194 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20195     
20196      /**
20197      * @cfg {Number} startDay
20198      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20199      */
20200     startDay : 0,
20201     
20202     loadMask : false,
20203     
20204     header : false,
20205       
20206     getAutoCreate : function(){
20207         
20208         
20209         var fc_button = function(name, corner, style, content ) {
20210             return Roo.apply({},{
20211                 tag : 'span',
20212                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20213                          (corner.length ?
20214                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20215                             ''
20216                         ),
20217                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20218                 unselectable: 'on'
20219             });
20220         };
20221         
20222         var header = {};
20223         
20224         if(!this.header){
20225             header = {
20226                 tag : 'table',
20227                 cls : 'fc-header',
20228                 style : 'width:100%',
20229                 cn : [
20230                     {
20231                         tag: 'tr',
20232                         cn : [
20233                             {
20234                                 tag : 'td',
20235                                 cls : 'fc-header-left',
20236                                 cn : [
20237                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20238                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20239                                     { tag: 'span', cls: 'fc-header-space' },
20240                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20241
20242
20243                                 ]
20244                             },
20245
20246                             {
20247                                 tag : 'td',
20248                                 cls : 'fc-header-center',
20249                                 cn : [
20250                                     {
20251                                         tag: 'span',
20252                                         cls: 'fc-header-title',
20253                                         cn : {
20254                                             tag: 'H2',
20255                                             html : 'month / year'
20256                                         }
20257                                     }
20258
20259                                 ]
20260                             },
20261                             {
20262                                 tag : 'td',
20263                                 cls : 'fc-header-right',
20264                                 cn : [
20265                               /*      fc_button('month', 'left', '', 'month' ),
20266                                     fc_button('week', '', '', 'week' ),
20267                                     fc_button('day', 'right', '', 'day' )
20268                                 */    
20269
20270                                 ]
20271                             }
20272
20273                         ]
20274                     }
20275                 ]
20276             };
20277         }
20278         
20279         header = this.header;
20280         
20281        
20282         var cal_heads = function() {
20283             var ret = [];
20284             // fixme - handle this.
20285             
20286             for (var i =0; i < Date.dayNames.length; i++) {
20287                 var d = Date.dayNames[i];
20288                 ret.push({
20289                     tag: 'th',
20290                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20291                     html : d.substring(0,3)
20292                 });
20293                 
20294             }
20295             ret[0].cls += ' fc-first';
20296             ret[6].cls += ' fc-last';
20297             return ret;
20298         };
20299         var cal_cell = function(n) {
20300             return  {
20301                 tag: 'td',
20302                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20303                 cn : [
20304                     {
20305                         cn : [
20306                             {
20307                                 cls: 'fc-day-number',
20308                                 html: 'D'
20309                             },
20310                             {
20311                                 cls: 'fc-day-content',
20312                              
20313                                 cn : [
20314                                      {
20315                                         style: 'position: relative;' // height: 17px;
20316                                     }
20317                                 ]
20318                             }
20319                             
20320                             
20321                         ]
20322                     }
20323                 ]
20324                 
20325             }
20326         };
20327         var cal_rows = function() {
20328             
20329             var ret = [];
20330             for (var r = 0; r < 6; r++) {
20331                 var row= {
20332                     tag : 'tr',
20333                     cls : 'fc-week',
20334                     cn : []
20335                 };
20336                 
20337                 for (var i =0; i < Date.dayNames.length; i++) {
20338                     var d = Date.dayNames[i];
20339                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20340
20341                 }
20342                 row.cn[0].cls+=' fc-first';
20343                 row.cn[0].cn[0].style = 'min-height:90px';
20344                 row.cn[6].cls+=' fc-last';
20345                 ret.push(row);
20346                 
20347             }
20348             ret[0].cls += ' fc-first';
20349             ret[4].cls += ' fc-prev-last';
20350             ret[5].cls += ' fc-last';
20351             return ret;
20352             
20353         };
20354         
20355         var cal_table = {
20356             tag: 'table',
20357             cls: 'fc-border-separate',
20358             style : 'width:100%',
20359             cellspacing  : 0,
20360             cn : [
20361                 { 
20362                     tag: 'thead',
20363                     cn : [
20364                         { 
20365                             tag: 'tr',
20366                             cls : 'fc-first fc-last',
20367                             cn : cal_heads()
20368                         }
20369                     ]
20370                 },
20371                 { 
20372                     tag: 'tbody',
20373                     cn : cal_rows()
20374                 }
20375                   
20376             ]
20377         };
20378          
20379          var cfg = {
20380             cls : 'fc fc-ltr',
20381             cn : [
20382                 header,
20383                 {
20384                     cls : 'fc-content',
20385                     style : "position: relative;",
20386                     cn : [
20387                         {
20388                             cls : 'fc-view fc-view-month fc-grid',
20389                             style : 'position: relative',
20390                             unselectable : 'on',
20391                             cn : [
20392                                 {
20393                                     cls : 'fc-event-container',
20394                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20395                                 },
20396                                 cal_table
20397                             ]
20398                         }
20399                     ]
20400     
20401                 }
20402            ] 
20403             
20404         };
20405         
20406          
20407         
20408         return cfg;
20409     },
20410     
20411     
20412     initEvents : function()
20413     {
20414         if(!this.store){
20415             throw "can not find store for calendar";
20416         }
20417         
20418         var mark = {
20419             tag: "div",
20420             cls:"x-dlg-mask",
20421             style: "text-align:center",
20422             cn: [
20423                 {
20424                     tag: "div",
20425                     style: "background-color:white;width:50%;margin:250 auto",
20426                     cn: [
20427                         {
20428                             tag: "img",
20429                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20430                         },
20431                         {
20432                             tag: "span",
20433                             html: "Loading"
20434                         }
20435                         
20436                     ]
20437                 }
20438             ]
20439         };
20440         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20441         
20442         var size = this.el.select('.fc-content', true).first().getSize();
20443         this.maskEl.setSize(size.width, size.height);
20444         this.maskEl.enableDisplayMode("block");
20445         if(!this.loadMask){
20446             this.maskEl.hide();
20447         }
20448         
20449         this.store = Roo.factory(this.store, Roo.data);
20450         this.store.on('load', this.onLoad, this);
20451         this.store.on('beforeload', this.onBeforeLoad, this);
20452         
20453         this.resize();
20454         
20455         this.cells = this.el.select('.fc-day',true);
20456         //Roo.log(this.cells);
20457         this.textNodes = this.el.query('.fc-day-number');
20458         this.cells.addClassOnOver('fc-state-hover');
20459         
20460         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20461         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20462         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20463         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20464         
20465         this.on('monthchange', this.onMonthChange, this);
20466         
20467         this.update(new Date().clearTime());
20468     },
20469     
20470     resize : function() {
20471         var sz  = this.el.getSize();
20472         
20473         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20474         this.el.select('.fc-day-content div',true).setHeight(34);
20475     },
20476     
20477     
20478     // private
20479     showPrevMonth : function(e){
20480         this.update(this.activeDate.add("mo", -1));
20481     },
20482     showToday : function(e){
20483         this.update(new Date().clearTime());
20484     },
20485     // private
20486     showNextMonth : function(e){
20487         this.update(this.activeDate.add("mo", 1));
20488     },
20489
20490     // private
20491     showPrevYear : function(){
20492         this.update(this.activeDate.add("y", -1));
20493     },
20494
20495     // private
20496     showNextYear : function(){
20497         this.update(this.activeDate.add("y", 1));
20498     },
20499
20500     
20501    // private
20502     update : function(date)
20503     {
20504         var vd = this.activeDate;
20505         this.activeDate = date;
20506 //        if(vd && this.el){
20507 //            var t = date.getTime();
20508 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20509 //                Roo.log('using add remove');
20510 //                
20511 //                this.fireEvent('monthchange', this, date);
20512 //                
20513 //                this.cells.removeClass("fc-state-highlight");
20514 //                this.cells.each(function(c){
20515 //                   if(c.dateValue == t){
20516 //                       c.addClass("fc-state-highlight");
20517 //                       setTimeout(function(){
20518 //                            try{c.dom.firstChild.focus();}catch(e){}
20519 //                       }, 50);
20520 //                       return false;
20521 //                   }
20522 //                   return true;
20523 //                });
20524 //                return;
20525 //            }
20526 //        }
20527         
20528         var days = date.getDaysInMonth();
20529         
20530         var firstOfMonth = date.getFirstDateOfMonth();
20531         var startingPos = firstOfMonth.getDay()-this.startDay;
20532         
20533         if(startingPos < this.startDay){
20534             startingPos += 7;
20535         }
20536         
20537         var pm = date.add(Date.MONTH, -1);
20538         var prevStart = pm.getDaysInMonth()-startingPos;
20539 //        
20540         this.cells = this.el.select('.fc-day',true);
20541         this.textNodes = this.el.query('.fc-day-number');
20542         this.cells.addClassOnOver('fc-state-hover');
20543         
20544         var cells = this.cells.elements;
20545         var textEls = this.textNodes;
20546         
20547         Roo.each(cells, function(cell){
20548             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20549         });
20550         
20551         days += startingPos;
20552
20553         // convert everything to numbers so it's fast
20554         var day = 86400000;
20555         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20556         //Roo.log(d);
20557         //Roo.log(pm);
20558         //Roo.log(prevStart);
20559         
20560         var today = new Date().clearTime().getTime();
20561         var sel = date.clearTime().getTime();
20562         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20563         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20564         var ddMatch = this.disabledDatesRE;
20565         var ddText = this.disabledDatesText;
20566         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20567         var ddaysText = this.disabledDaysText;
20568         var format = this.format;
20569         
20570         var setCellClass = function(cal, cell){
20571             cell.row = 0;
20572             cell.events = [];
20573             cell.more = [];
20574             //Roo.log('set Cell Class');
20575             cell.title = "";
20576             var t = d.getTime();
20577             
20578             //Roo.log(d);
20579             
20580             cell.dateValue = t;
20581             if(t == today){
20582                 cell.className += " fc-today";
20583                 cell.className += " fc-state-highlight";
20584                 cell.title = cal.todayText;
20585             }
20586             if(t == sel){
20587                 // disable highlight in other month..
20588                 //cell.className += " fc-state-highlight";
20589                 
20590             }
20591             // disabling
20592             if(t < min) {
20593                 cell.className = " fc-state-disabled";
20594                 cell.title = cal.minText;
20595                 return;
20596             }
20597             if(t > max) {
20598                 cell.className = " fc-state-disabled";
20599                 cell.title = cal.maxText;
20600                 return;
20601             }
20602             if(ddays){
20603                 if(ddays.indexOf(d.getDay()) != -1){
20604                     cell.title = ddaysText;
20605                     cell.className = " fc-state-disabled";
20606                 }
20607             }
20608             if(ddMatch && format){
20609                 var fvalue = d.dateFormat(format);
20610                 if(ddMatch.test(fvalue)){
20611                     cell.title = ddText.replace("%0", fvalue);
20612                     cell.className = " fc-state-disabled";
20613                 }
20614             }
20615             
20616             if (!cell.initialClassName) {
20617                 cell.initialClassName = cell.dom.className;
20618             }
20619             
20620             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20621         };
20622
20623         var i = 0;
20624         
20625         for(; i < startingPos; i++) {
20626             textEls[i].innerHTML = (++prevStart);
20627             d.setDate(d.getDate()+1);
20628             
20629             cells[i].className = "fc-past fc-other-month";
20630             setCellClass(this, cells[i]);
20631         }
20632         
20633         var intDay = 0;
20634         
20635         for(; i < days; i++){
20636             intDay = i - startingPos + 1;
20637             textEls[i].innerHTML = (intDay);
20638             d.setDate(d.getDate()+1);
20639             
20640             cells[i].className = ''; // "x-date-active";
20641             setCellClass(this, cells[i]);
20642         }
20643         var extraDays = 0;
20644         
20645         for(; i < 42; i++) {
20646             textEls[i].innerHTML = (++extraDays);
20647             d.setDate(d.getDate()+1);
20648             
20649             cells[i].className = "fc-future fc-other-month";
20650             setCellClass(this, cells[i]);
20651         }
20652         
20653         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20654         
20655         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20656         
20657         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20658         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20659         
20660         if(totalRows != 6){
20661             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20662             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20663         }
20664         
20665         this.fireEvent('monthchange', this, date);
20666         
20667         
20668         /*
20669         if(!this.internalRender){
20670             var main = this.el.dom.firstChild;
20671             var w = main.offsetWidth;
20672             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20673             Roo.fly(main).setWidth(w);
20674             this.internalRender = true;
20675             // opera does not respect the auto grow header center column
20676             // then, after it gets a width opera refuses to recalculate
20677             // without a second pass
20678             if(Roo.isOpera && !this.secondPass){
20679                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20680                 this.secondPass = true;
20681                 this.update.defer(10, this, [date]);
20682             }
20683         }
20684         */
20685         
20686     },
20687     
20688     findCell : function(dt) {
20689         dt = dt.clearTime().getTime();
20690         var ret = false;
20691         this.cells.each(function(c){
20692             //Roo.log("check " +c.dateValue + '?=' + dt);
20693             if(c.dateValue == dt){
20694                 ret = c;
20695                 return false;
20696             }
20697             return true;
20698         });
20699         
20700         return ret;
20701     },
20702     
20703     findCells : function(ev) {
20704         var s = ev.start.clone().clearTime().getTime();
20705        // Roo.log(s);
20706         var e= ev.end.clone().clearTime().getTime();
20707        // Roo.log(e);
20708         var ret = [];
20709         this.cells.each(function(c){
20710              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20711             
20712             if(c.dateValue > e){
20713                 return ;
20714             }
20715             if(c.dateValue < s){
20716                 return ;
20717             }
20718             ret.push(c);
20719         });
20720         
20721         return ret;    
20722     },
20723     
20724 //    findBestRow: function(cells)
20725 //    {
20726 //        var ret = 0;
20727 //        
20728 //        for (var i =0 ; i < cells.length;i++) {
20729 //            ret  = Math.max(cells[i].rows || 0,ret);
20730 //        }
20731 //        return ret;
20732 //        
20733 //    },
20734     
20735     
20736     addItem : function(ev)
20737     {
20738         // look for vertical location slot in
20739         var cells = this.findCells(ev);
20740         
20741 //        ev.row = this.findBestRow(cells);
20742         
20743         // work out the location.
20744         
20745         var crow = false;
20746         var rows = [];
20747         for(var i =0; i < cells.length; i++) {
20748             
20749             cells[i].row = cells[0].row;
20750             
20751             if(i == 0){
20752                 cells[i].row = cells[i].row + 1;
20753             }
20754             
20755             if (!crow) {
20756                 crow = {
20757                     start : cells[i],
20758                     end :  cells[i]
20759                 };
20760                 continue;
20761             }
20762             if (crow.start.getY() == cells[i].getY()) {
20763                 // on same row.
20764                 crow.end = cells[i];
20765                 continue;
20766             }
20767             // different row.
20768             rows.push(crow);
20769             crow = {
20770                 start: cells[i],
20771                 end : cells[i]
20772             };
20773             
20774         }
20775         
20776         rows.push(crow);
20777         ev.els = [];
20778         ev.rows = rows;
20779         ev.cells = cells;
20780         
20781         cells[0].events.push(ev);
20782         
20783         this.calevents.push(ev);
20784     },
20785     
20786     clearEvents: function() {
20787         
20788         if(!this.calevents){
20789             return;
20790         }
20791         
20792         Roo.each(this.cells.elements, function(c){
20793             c.row = 0;
20794             c.events = [];
20795             c.more = [];
20796         });
20797         
20798         Roo.each(this.calevents, function(e) {
20799             Roo.each(e.els, function(el) {
20800                 el.un('mouseenter' ,this.onEventEnter, this);
20801                 el.un('mouseleave' ,this.onEventLeave, this);
20802                 el.remove();
20803             },this);
20804         },this);
20805         
20806         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20807             e.remove();
20808         });
20809         
20810     },
20811     
20812     renderEvents: function()
20813     {   
20814         var _this = this;
20815         
20816         this.cells.each(function(c) {
20817             
20818             if(c.row < 5){
20819                 return;
20820             }
20821             
20822             var ev = c.events;
20823             
20824             var r = 4;
20825             if(c.row != c.events.length){
20826                 r = 4 - (4 - (c.row - c.events.length));
20827             }
20828             
20829             c.events = ev.slice(0, r);
20830             c.more = ev.slice(r);
20831             
20832             if(c.more.length && c.more.length == 1){
20833                 c.events.push(c.more.pop());
20834             }
20835             
20836             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20837             
20838         });
20839             
20840         this.cells.each(function(c) {
20841             
20842             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20843             
20844             
20845             for (var e = 0; e < c.events.length; e++){
20846                 var ev = c.events[e];
20847                 var rows = ev.rows;
20848                 
20849                 for(var i = 0; i < rows.length; i++) {
20850                 
20851                     // how many rows should it span..
20852
20853                     var  cfg = {
20854                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20855                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20856
20857                         unselectable : "on",
20858                         cn : [
20859                             {
20860                                 cls: 'fc-event-inner',
20861                                 cn : [
20862     //                                {
20863     //                                  tag:'span',
20864     //                                  cls: 'fc-event-time',
20865     //                                  html : cells.length > 1 ? '' : ev.time
20866     //                                },
20867                                     {
20868                                       tag:'span',
20869                                       cls: 'fc-event-title',
20870                                       html : String.format('{0}', ev.title)
20871                                     }
20872
20873
20874                                 ]
20875                             },
20876                             {
20877                                 cls: 'ui-resizable-handle ui-resizable-e',
20878                                 html : '&nbsp;&nbsp;&nbsp'
20879                             }
20880
20881                         ]
20882                     };
20883
20884                     if (i == 0) {
20885                         cfg.cls += ' fc-event-start';
20886                     }
20887                     if ((i+1) == rows.length) {
20888                         cfg.cls += ' fc-event-end';
20889                     }
20890
20891                     var ctr = _this.el.select('.fc-event-container',true).first();
20892                     var cg = ctr.createChild(cfg);
20893
20894                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20895                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20896
20897                     var r = (c.more.length) ? 1 : 0;
20898                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20899                     cg.setWidth(ebox.right - sbox.x -2);
20900
20901                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20902                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20903                     cg.on('click', _this.onEventClick, _this, ev);
20904
20905                     ev.els.push(cg);
20906                     
20907                 }
20908                 
20909             }
20910             
20911             
20912             if(c.more.length){
20913                 var  cfg = {
20914                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20915                     style : 'position: absolute',
20916                     unselectable : "on",
20917                     cn : [
20918                         {
20919                             cls: 'fc-event-inner',
20920                             cn : [
20921                                 {
20922                                   tag:'span',
20923                                   cls: 'fc-event-title',
20924                                   html : 'More'
20925                                 }
20926
20927
20928                             ]
20929                         },
20930                         {
20931                             cls: 'ui-resizable-handle ui-resizable-e',
20932                             html : '&nbsp;&nbsp;&nbsp'
20933                         }
20934
20935                     ]
20936                 };
20937
20938                 var ctr = _this.el.select('.fc-event-container',true).first();
20939                 var cg = ctr.createChild(cfg);
20940
20941                 var sbox = c.select('.fc-day-content',true).first().getBox();
20942                 var ebox = c.select('.fc-day-content',true).first().getBox();
20943                 //Roo.log(cg);
20944                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20945                 cg.setWidth(ebox.right - sbox.x -2);
20946
20947                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20948                 
20949             }
20950             
20951         });
20952         
20953         
20954         
20955     },
20956     
20957     onEventEnter: function (e, el,event,d) {
20958         this.fireEvent('evententer', this, el, event);
20959     },
20960     
20961     onEventLeave: function (e, el,event,d) {
20962         this.fireEvent('eventleave', this, el, event);
20963     },
20964     
20965     onEventClick: function (e, el,event,d) {
20966         this.fireEvent('eventclick', this, el, event);
20967     },
20968     
20969     onMonthChange: function () {
20970         this.store.load();
20971     },
20972     
20973     onMoreEventClick: function(e, el, more)
20974     {
20975         var _this = this;
20976         
20977         this.calpopover.placement = 'right';
20978         this.calpopover.setTitle('More');
20979         
20980         this.calpopover.setContent('');
20981         
20982         var ctr = this.calpopover.el.select('.popover-content', true).first();
20983         
20984         Roo.each(more, function(m){
20985             var cfg = {
20986                 cls : 'fc-event-hori fc-event-draggable',
20987                 html : m.title
20988             };
20989             var cg = ctr.createChild(cfg);
20990             
20991             cg.on('click', _this.onEventClick, _this, m);
20992         });
20993         
20994         this.calpopover.show(el);
20995         
20996         
20997     },
20998     
20999     onLoad: function () 
21000     {   
21001         this.calevents = [];
21002         var cal = this;
21003         
21004         if(this.store.getCount() > 0){
21005             this.store.data.each(function(d){
21006                cal.addItem({
21007                     id : d.data.id,
21008                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21009                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21010                     time : d.data.start_time,
21011                     title : d.data.title,
21012                     description : d.data.description,
21013                     venue : d.data.venue
21014                 });
21015             });
21016         }
21017         
21018         this.renderEvents();
21019         
21020         if(this.calevents.length && this.loadMask){
21021             this.maskEl.hide();
21022         }
21023     },
21024     
21025     onBeforeLoad: function()
21026     {
21027         this.clearEvents();
21028         if(this.loadMask){
21029             this.maskEl.show();
21030         }
21031     }
21032 });
21033
21034  
21035  /*
21036  * - LGPL
21037  *
21038  * element
21039  * 
21040  */
21041
21042 /**
21043  * @class Roo.bootstrap.Popover
21044  * @extends Roo.bootstrap.Component
21045  * Bootstrap Popover class
21046  * @cfg {String} html contents of the popover   (or false to use children..)
21047  * @cfg {String} title of popover (or false to hide)
21048  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21049  * @cfg {String} trigger click || hover (or false to trigger manually)
21050  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21051  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21052  *      - if false and it has a 'parent' then it will be automatically added to that element
21053  *      - if string - Roo.get  will be called 
21054  * @cfg {Number} delay - delay before showing
21055  
21056  * @constructor
21057  * Create a new Popover
21058  * @param {Object} config The config object
21059  */
21060
21061 Roo.bootstrap.Popover = function(config){
21062     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21063     
21064     this.addEvents({
21065         // raw events
21066          /**
21067          * @event show
21068          * After the popover show
21069          * 
21070          * @param {Roo.bootstrap.Popover} this
21071          */
21072         "show" : true,
21073         /**
21074          * @event hide
21075          * After the popover hide
21076          * 
21077          * @param {Roo.bootstrap.Popover} this
21078          */
21079         "hide" : true
21080     });
21081 };
21082
21083 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21084     
21085     title: false,
21086     html: false,
21087     
21088     placement : 'right',
21089     trigger : 'hover', // hover
21090     modal : false,
21091     delay : 0,
21092     
21093     over: false,
21094     
21095     can_build_overlaid : false,
21096     
21097     maskEl : false, // the mask element
21098     headerEl : false,
21099     contentEl : false,
21100     alignEl : false, // when show is called with an element - this get's stored.
21101     
21102     getChildContainer : function()
21103     {
21104         return this.contentEl;
21105         
21106     },
21107     getPopoverHeader : function()
21108     {
21109         this.title = true; // flag not to hide it..
21110         this.headerEl.addClass('p-0');
21111         return this.headerEl
21112     },
21113     
21114     
21115     getAutoCreate : function(){
21116          
21117         var cfg = {
21118            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21119            style: 'display:block',
21120            cn : [
21121                 {
21122                     cls : 'arrow'
21123                 },
21124                 {
21125                     cls : 'popover-inner ',
21126                     cn : [
21127                         {
21128                             tag: 'h3',
21129                             cls: 'popover-title popover-header',
21130                             html : this.title === false ? '' : this.title
21131                         },
21132                         {
21133                             cls : 'popover-content popover-body '  + (this.cls || ''),
21134                             html : this.html || ''
21135                         }
21136                     ]
21137                     
21138                 }
21139            ]
21140         };
21141         
21142         return cfg;
21143     },
21144     /**
21145      * @param {string} the title
21146      */
21147     setTitle: function(str)
21148     {
21149         this.title = str;
21150         if (this.el) {
21151             this.headerEl.dom.innerHTML = str;
21152         }
21153         
21154     },
21155     /**
21156      * @param {string} the body content
21157      */
21158     setContent: function(str)
21159     {
21160         this.html = str;
21161         if (this.contentEl) {
21162             this.contentEl.dom.innerHTML = str;
21163         }
21164         
21165     },
21166     // as it get's added to the bottom of the page.
21167     onRender : function(ct, position)
21168     {
21169         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21170         
21171         
21172         
21173         if(!this.el){
21174             var cfg = Roo.apply({},  this.getAutoCreate());
21175             cfg.id = Roo.id();
21176             
21177             if (this.cls) {
21178                 cfg.cls += ' ' + this.cls;
21179             }
21180             if (this.style) {
21181                 cfg.style = this.style;
21182             }
21183             //Roo.log("adding to ");
21184             this.el = Roo.get(document.body).createChild(cfg, position);
21185 //            Roo.log(this.el);
21186         }
21187         
21188         this.contentEl = this.el.select('.popover-content',true).first();
21189         this.headerEl =  this.el.select('.popover-title',true).first();
21190         
21191         var nitems = [];
21192         if(typeof(this.items) != 'undefined'){
21193             var items = this.items;
21194             delete this.items;
21195
21196             for(var i =0;i < items.length;i++) {
21197                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21198             }
21199         }
21200
21201         this.items = nitems;
21202         
21203         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21204         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21205         
21206         
21207         
21208         this.initEvents();
21209     },
21210     
21211     resizeMask : function()
21212     {
21213         this.maskEl.setSize(
21214             Roo.lib.Dom.getViewWidth(true),
21215             Roo.lib.Dom.getViewHeight(true)
21216         );
21217     },
21218     
21219     initEvents : function()
21220     {
21221         
21222         if (!this.modal) { 
21223             Roo.bootstrap.Popover.register(this);
21224         }
21225          
21226         this.arrowEl = this.el.select('.arrow',true).first();
21227         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21228         this.el.enableDisplayMode('block');
21229         this.el.hide();
21230  
21231         
21232         if (this.over === false && !this.parent()) {
21233             return; 
21234         }
21235         if (this.triggers === false) {
21236             return;
21237         }
21238          
21239         // support parent
21240         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21241         var triggers = this.trigger ? this.trigger.split(' ') : [];
21242         Roo.each(triggers, function(trigger) {
21243         
21244             if (trigger == 'click') {
21245                 on_el.on('click', this.toggle, this);
21246             } else if (trigger != 'manual') {
21247                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21248                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21249       
21250                 on_el.on(eventIn  ,this.enter, this);
21251                 on_el.on(eventOut, this.leave, this);
21252             }
21253         }, this);
21254     },
21255     
21256     
21257     // private
21258     timeout : null,
21259     hoverState : null,
21260     
21261     toggle : function () {
21262         this.hoverState == 'in' ? this.leave() : this.enter();
21263     },
21264     
21265     enter : function () {
21266         
21267         clearTimeout(this.timeout);
21268     
21269         this.hoverState = 'in';
21270     
21271         if (!this.delay || !this.delay.show) {
21272             this.show();
21273             return;
21274         }
21275         var _t = this;
21276         this.timeout = setTimeout(function () {
21277             if (_t.hoverState == 'in') {
21278                 _t.show();
21279             }
21280         }, this.delay.show)
21281     },
21282     
21283     leave : function() {
21284         clearTimeout(this.timeout);
21285     
21286         this.hoverState = 'out';
21287     
21288         if (!this.delay || !this.delay.hide) {
21289             this.hide();
21290             return;
21291         }
21292         var _t = this;
21293         this.timeout = setTimeout(function () {
21294             if (_t.hoverState == 'out') {
21295                 _t.hide();
21296             }
21297         }, this.delay.hide)
21298     },
21299     /**
21300      * Show the popover
21301      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21302      * @param {string} (left|right|top|bottom) position
21303      */
21304     show : function (on_el, placement)
21305     {
21306         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21307         on_el = on_el || false; // default to false
21308          
21309         if (!on_el) {
21310             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21311                 on_el = this.parent().el;
21312             } else if (this.over) {
21313                 on_el = Roo.get(this.over);
21314             }
21315             
21316         }
21317         
21318         this.alignEl = Roo.get( on_el );
21319
21320         if (!this.el) {
21321             this.render(document.body);
21322         }
21323         
21324         
21325          
21326         
21327         if (this.title === false) {
21328             this.headerEl.hide();
21329         }
21330         
21331        
21332         this.el.show();
21333         this.el.dom.style.display = 'block';
21334          
21335  
21336         if (this.alignEl) {
21337             this.updatePosition(this.placement, true);
21338              
21339         } else {
21340             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21341             var es = this.el.getSize();
21342             var x = Roo.lib.Dom.getViewWidth()/2;
21343             var y = Roo.lib.Dom.getViewHeight()/2;
21344             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21345             
21346         }
21347
21348         
21349         //var arrow = this.el.select('.arrow',true).first();
21350         //arrow.set(align[2], 
21351         
21352         this.el.addClass('in');
21353         
21354          
21355         
21356         this.hoverState = 'in';
21357         
21358         if (this.modal) {
21359             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21360             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21361             this.maskEl.dom.style.display = 'block';
21362             this.maskEl.addClass('show');
21363         }
21364         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21365  
21366         this.fireEvent('show', this);
21367         
21368     },
21369     /**
21370      * fire this manually after loading a grid in the table for example
21371      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21372      * @param {Boolean} try and move it if we cant get right position.
21373      */
21374     updatePosition : function(placement, try_move)
21375     {
21376         // allow for calling with no parameters
21377         placement = placement   ? placement :  this.placement;
21378         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21379         
21380         this.el.removeClass([
21381             'fade','top','bottom', 'left', 'right','in',
21382             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21383         ]);
21384         this.el.addClass(placement + ' bs-popover-' + placement);
21385         
21386         if (!this.alignEl ) {
21387             return false;
21388         }
21389         
21390         switch (placement) {
21391             case 'right':
21392                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21393                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21394                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21395                     //normal display... or moved up/down.
21396                     this.el.setXY(offset);
21397                     var xy = this.alignEl.getAnchorXY('tr', false);
21398                     xy[0]+=2;xy[1]+=5;
21399                     this.arrowEl.setXY(xy);
21400                     return true;
21401                 }
21402                 // continue through...
21403                 return this.updatePosition('left', false);
21404                 
21405             
21406             case 'left':
21407                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21408                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21409                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21410                     //normal display... or moved up/down.
21411                     this.el.setXY(offset);
21412                     var xy = this.alignEl.getAnchorXY('tl', false);
21413                     xy[0]-=10;xy[1]+=5; // << fix me
21414                     this.arrowEl.setXY(xy);
21415                     return true;
21416                 }
21417                 // call self...
21418                 return this.updatePosition('right', false);
21419             
21420             case 'top':
21421                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21422                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21423                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21424                     //normal display... or moved up/down.
21425                     this.el.setXY(offset);
21426                     var xy = this.alignEl.getAnchorXY('t', false);
21427                     xy[1]-=10; // << fix me
21428                     this.arrowEl.setXY(xy);
21429                     return true;
21430                 }
21431                 // fall through
21432                return this.updatePosition('bottom', false);
21433             
21434             case 'bottom':
21435                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21436                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21437                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21438                     //normal display... or moved up/down.
21439                     this.el.setXY(offset);
21440                     var xy = this.alignEl.getAnchorXY('b', false);
21441                      xy[1]+=2; // << fix me
21442                     this.arrowEl.setXY(xy);
21443                     return true;
21444                 }
21445                 // fall through
21446                 return this.updatePosition('top', false);
21447                 
21448             
21449         }
21450         
21451         
21452         return false;
21453     },
21454     
21455     hide : function()
21456     {
21457         this.el.setXY([0,0]);
21458         this.el.removeClass('in');
21459         this.el.hide();
21460         this.hoverState = null;
21461         this.maskEl.hide(); // always..
21462         this.fireEvent('hide', this);
21463     }
21464     
21465 });
21466
21467
21468 Roo.apply(Roo.bootstrap.Popover, {
21469
21470     alignment : {
21471         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21472         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21473         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21474         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21475     },
21476     
21477     zIndex : 20001,
21478
21479     clickHander : false,
21480     
21481     
21482
21483     onMouseDown : function(e)
21484     {
21485         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21486             /// what is nothing is showing..
21487             this.hideAll();
21488         }
21489          
21490     },
21491     
21492     
21493     popups : [],
21494     
21495     register : function(popup)
21496     {
21497         if (!Roo.bootstrap.Popover.clickHandler) {
21498             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21499         }
21500         // hide other popups.
21501         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21502         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21503         this.hideAll(); //<< why?
21504         //this.popups.push(popup);
21505     },
21506     hideAll : function()
21507     {
21508         this.popups.forEach(function(p) {
21509             p.hide();
21510         });
21511     },
21512     onShow : function() {
21513         Roo.bootstrap.Popover.popups.push(this);
21514     },
21515     onHide : function() {
21516         Roo.bootstrap.Popover.popups.remove(this);
21517     } 
21518
21519 });/*
21520  * - LGPL
21521  *
21522  * Card header - holder for the card header elements.
21523  * 
21524  */
21525
21526 /**
21527  * @class Roo.bootstrap.PopoverNav
21528  * @extends Roo.bootstrap.NavGroup
21529  * Bootstrap Popover header navigation class
21530  * @constructor
21531  * Create a new Popover Header Navigation 
21532  * @param {Object} config The config object
21533  */
21534
21535 Roo.bootstrap.PopoverNav = function(config){
21536     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21537 };
21538
21539 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21540     
21541     
21542     container_method : 'getPopoverHeader' 
21543     
21544      
21545     
21546     
21547    
21548 });
21549
21550  
21551
21552  /*
21553  * - LGPL
21554  *
21555  * Progress
21556  * 
21557  */
21558
21559 /**
21560  * @class Roo.bootstrap.Progress
21561  * @extends Roo.bootstrap.Component
21562  * Bootstrap Progress class
21563  * @cfg {Boolean} striped striped of the progress bar
21564  * @cfg {Boolean} active animated of the progress bar
21565  * 
21566  * 
21567  * @constructor
21568  * Create a new Progress
21569  * @param {Object} config The config object
21570  */
21571
21572 Roo.bootstrap.Progress = function(config){
21573     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21574 };
21575
21576 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21577     
21578     striped : false,
21579     active: false,
21580     
21581     getAutoCreate : function(){
21582         var cfg = {
21583             tag: 'div',
21584             cls: 'progress'
21585         };
21586         
21587         
21588         if(this.striped){
21589             cfg.cls += ' progress-striped';
21590         }
21591       
21592         if(this.active){
21593             cfg.cls += ' active';
21594         }
21595         
21596         
21597         return cfg;
21598     }
21599    
21600 });
21601
21602  
21603
21604  /*
21605  * - LGPL
21606  *
21607  * ProgressBar
21608  * 
21609  */
21610
21611 /**
21612  * @class Roo.bootstrap.ProgressBar
21613  * @extends Roo.bootstrap.Component
21614  * Bootstrap ProgressBar class
21615  * @cfg {Number} aria_valuenow aria-value now
21616  * @cfg {Number} aria_valuemin aria-value min
21617  * @cfg {Number} aria_valuemax aria-value max
21618  * @cfg {String} label label for the progress bar
21619  * @cfg {String} panel (success | info | warning | danger )
21620  * @cfg {String} role role of the progress bar
21621  * @cfg {String} sr_only text
21622  * 
21623  * 
21624  * @constructor
21625  * Create a new ProgressBar
21626  * @param {Object} config The config object
21627  */
21628
21629 Roo.bootstrap.ProgressBar = function(config){
21630     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21631 };
21632
21633 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21634     
21635     aria_valuenow : 0,
21636     aria_valuemin : 0,
21637     aria_valuemax : 100,
21638     label : false,
21639     panel : false,
21640     role : false,
21641     sr_only: false,
21642     
21643     getAutoCreate : function()
21644     {
21645         
21646         var cfg = {
21647             tag: 'div',
21648             cls: 'progress-bar',
21649             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21650         };
21651         
21652         if(this.sr_only){
21653             cfg.cn = {
21654                 tag: 'span',
21655                 cls: 'sr-only',
21656                 html: this.sr_only
21657             }
21658         }
21659         
21660         if(this.role){
21661             cfg.role = this.role;
21662         }
21663         
21664         if(this.aria_valuenow){
21665             cfg['aria-valuenow'] = this.aria_valuenow;
21666         }
21667         
21668         if(this.aria_valuemin){
21669             cfg['aria-valuemin'] = this.aria_valuemin;
21670         }
21671         
21672         if(this.aria_valuemax){
21673             cfg['aria-valuemax'] = this.aria_valuemax;
21674         }
21675         
21676         if(this.label && !this.sr_only){
21677             cfg.html = this.label;
21678         }
21679         
21680         if(this.panel){
21681             cfg.cls += ' progress-bar-' + this.panel;
21682         }
21683         
21684         return cfg;
21685     },
21686     
21687     update : function(aria_valuenow)
21688     {
21689         this.aria_valuenow = aria_valuenow;
21690         
21691         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21692     }
21693    
21694 });
21695
21696  
21697
21698  /*
21699  * - LGPL
21700  *
21701  * column
21702  * 
21703  */
21704
21705 /**
21706  * @class Roo.bootstrap.TabGroup
21707  * @extends Roo.bootstrap.Column
21708  * Bootstrap Column class
21709  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21710  * @cfg {Boolean} carousel true to make the group behave like a carousel
21711  * @cfg {Boolean} bullets show bullets for the panels
21712  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21713  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21714  * @cfg {Boolean} showarrow (true|false) show arrow default true
21715  * 
21716  * @constructor
21717  * Create a new TabGroup
21718  * @param {Object} config The config object
21719  */
21720
21721 Roo.bootstrap.TabGroup = function(config){
21722     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21723     if (!this.navId) {
21724         this.navId = Roo.id();
21725     }
21726     this.tabs = [];
21727     Roo.bootstrap.TabGroup.register(this);
21728     
21729 };
21730
21731 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21732     
21733     carousel : false,
21734     transition : false,
21735     bullets : 0,
21736     timer : 0,
21737     autoslide : false,
21738     slideFn : false,
21739     slideOnTouch : false,
21740     showarrow : true,
21741     
21742     getAutoCreate : function()
21743     {
21744         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21745         
21746         cfg.cls += ' tab-content';
21747         
21748         if (this.carousel) {
21749             cfg.cls += ' carousel slide';
21750             
21751             cfg.cn = [{
21752                cls : 'carousel-inner',
21753                cn : []
21754             }];
21755         
21756             if(this.bullets  && !Roo.isTouch){
21757                 
21758                 var bullets = {
21759                     cls : 'carousel-bullets',
21760                     cn : []
21761                 };
21762                
21763                 if(this.bullets_cls){
21764                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21765                 }
21766                 
21767                 bullets.cn.push({
21768                     cls : 'clear'
21769                 });
21770                 
21771                 cfg.cn[0].cn.push(bullets);
21772             }
21773             
21774             if(this.showarrow){
21775                 cfg.cn[0].cn.push({
21776                     tag : 'div',
21777                     class : 'carousel-arrow',
21778                     cn : [
21779                         {
21780                             tag : 'div',
21781                             class : 'carousel-prev',
21782                             cn : [
21783                                 {
21784                                     tag : 'i',
21785                                     class : 'fa fa-chevron-left'
21786                                 }
21787                             ]
21788                         },
21789                         {
21790                             tag : 'div',
21791                             class : 'carousel-next',
21792                             cn : [
21793                                 {
21794                                     tag : 'i',
21795                                     class : 'fa fa-chevron-right'
21796                                 }
21797                             ]
21798                         }
21799                     ]
21800                 });
21801             }
21802             
21803         }
21804         
21805         return cfg;
21806     },
21807     
21808     initEvents:  function()
21809     {
21810 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21811 //            this.el.on("touchstart", this.onTouchStart, this);
21812 //        }
21813         
21814         if(this.autoslide){
21815             var _this = this;
21816             
21817             this.slideFn = window.setInterval(function() {
21818                 _this.showPanelNext();
21819             }, this.timer);
21820         }
21821         
21822         if(this.showarrow){
21823             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21824             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21825         }
21826         
21827         
21828     },
21829     
21830 //    onTouchStart : function(e, el, o)
21831 //    {
21832 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21833 //            return;
21834 //        }
21835 //        
21836 //        this.showPanelNext();
21837 //    },
21838     
21839     
21840     getChildContainer : function()
21841     {
21842         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21843     },
21844     
21845     /**
21846     * register a Navigation item
21847     * @param {Roo.bootstrap.NavItem} the navitem to add
21848     */
21849     register : function(item)
21850     {
21851         this.tabs.push( item);
21852         item.navId = this.navId; // not really needed..
21853         this.addBullet();
21854     
21855     },
21856     
21857     getActivePanel : function()
21858     {
21859         var r = false;
21860         Roo.each(this.tabs, function(t) {
21861             if (t.active) {
21862                 r = t;
21863                 return false;
21864             }
21865             return null;
21866         });
21867         return r;
21868         
21869     },
21870     getPanelByName : function(n)
21871     {
21872         var r = false;
21873         Roo.each(this.tabs, function(t) {
21874             if (t.tabId == n) {
21875                 r = t;
21876                 return false;
21877             }
21878             return null;
21879         });
21880         return r;
21881     },
21882     indexOfPanel : function(p)
21883     {
21884         var r = false;
21885         Roo.each(this.tabs, function(t,i) {
21886             if (t.tabId == p.tabId) {
21887                 r = i;
21888                 return false;
21889             }
21890             return null;
21891         });
21892         return r;
21893     },
21894     /**
21895      * show a specific panel
21896      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21897      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21898      */
21899     showPanel : function (pan)
21900     {
21901         if(this.transition || typeof(pan) == 'undefined'){
21902             Roo.log("waiting for the transitionend");
21903             return false;
21904         }
21905         
21906         if (typeof(pan) == 'number') {
21907             pan = this.tabs[pan];
21908         }
21909         
21910         if (typeof(pan) == 'string') {
21911             pan = this.getPanelByName(pan);
21912         }
21913         
21914         var cur = this.getActivePanel();
21915         
21916         if(!pan || !cur){
21917             Roo.log('pan or acitve pan is undefined');
21918             return false;
21919         }
21920         
21921         if (pan.tabId == this.getActivePanel().tabId) {
21922             return true;
21923         }
21924         
21925         if (false === cur.fireEvent('beforedeactivate')) {
21926             return false;
21927         }
21928         
21929         if(this.bullets > 0 && !Roo.isTouch){
21930             this.setActiveBullet(this.indexOfPanel(pan));
21931         }
21932         
21933         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21934             
21935             //class="carousel-item carousel-item-next carousel-item-left"
21936             
21937             this.transition = true;
21938             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21939             var lr = dir == 'next' ? 'left' : 'right';
21940             pan.el.addClass(dir); // or prev
21941             pan.el.addClass('carousel-item-' + dir); // or prev
21942             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21943             cur.el.addClass(lr); // or right
21944             pan.el.addClass(lr);
21945             cur.el.addClass('carousel-item-' +lr); // or right
21946             pan.el.addClass('carousel-item-' +lr);
21947             
21948             
21949             var _this = this;
21950             cur.el.on('transitionend', function() {
21951                 Roo.log("trans end?");
21952                 
21953                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21954                 pan.setActive(true);
21955                 
21956                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21957                 cur.setActive(false);
21958                 
21959                 _this.transition = false;
21960                 
21961             }, this, { single:  true } );
21962             
21963             return true;
21964         }
21965         
21966         cur.setActive(false);
21967         pan.setActive(true);
21968         
21969         return true;
21970         
21971     },
21972     showPanelNext : function()
21973     {
21974         var i = this.indexOfPanel(this.getActivePanel());
21975         
21976         if (i >= this.tabs.length - 1 && !this.autoslide) {
21977             return;
21978         }
21979         
21980         if (i >= this.tabs.length - 1 && this.autoslide) {
21981             i = -1;
21982         }
21983         
21984         this.showPanel(this.tabs[i+1]);
21985     },
21986     
21987     showPanelPrev : function()
21988     {
21989         var i = this.indexOfPanel(this.getActivePanel());
21990         
21991         if (i  < 1 && !this.autoslide) {
21992             return;
21993         }
21994         
21995         if (i < 1 && this.autoslide) {
21996             i = this.tabs.length;
21997         }
21998         
21999         this.showPanel(this.tabs[i-1]);
22000     },
22001     
22002     
22003     addBullet: function()
22004     {
22005         if(!this.bullets || Roo.isTouch){
22006             return;
22007         }
22008         var ctr = this.el.select('.carousel-bullets',true).first();
22009         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22010         var bullet = ctr.createChild({
22011             cls : 'bullet bullet-' + i
22012         },ctr.dom.lastChild);
22013         
22014         
22015         var _this = this;
22016         
22017         bullet.on('click', (function(e, el, o, ii, t){
22018
22019             e.preventDefault();
22020
22021             this.showPanel(ii);
22022
22023             if(this.autoslide && this.slideFn){
22024                 clearInterval(this.slideFn);
22025                 this.slideFn = window.setInterval(function() {
22026                     _this.showPanelNext();
22027                 }, this.timer);
22028             }
22029
22030         }).createDelegate(this, [i, bullet], true));
22031                 
22032         
22033     },
22034      
22035     setActiveBullet : function(i)
22036     {
22037         if(Roo.isTouch){
22038             return;
22039         }
22040         
22041         Roo.each(this.el.select('.bullet', true).elements, function(el){
22042             el.removeClass('selected');
22043         });
22044
22045         var bullet = this.el.select('.bullet-' + i, true).first();
22046         
22047         if(!bullet){
22048             return;
22049         }
22050         
22051         bullet.addClass('selected');
22052     }
22053     
22054     
22055   
22056 });
22057
22058  
22059
22060  
22061  
22062 Roo.apply(Roo.bootstrap.TabGroup, {
22063     
22064     groups: {},
22065      /**
22066     * register a Navigation Group
22067     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22068     */
22069     register : function(navgrp)
22070     {
22071         this.groups[navgrp.navId] = navgrp;
22072         
22073     },
22074     /**
22075     * fetch a Navigation Group based on the navigation ID
22076     * if one does not exist , it will get created.
22077     * @param {string} the navgroup to add
22078     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22079     */
22080     get: function(navId) {
22081         if (typeof(this.groups[navId]) == 'undefined') {
22082             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22083         }
22084         return this.groups[navId] ;
22085     }
22086     
22087     
22088     
22089 });
22090
22091  /*
22092  * - LGPL
22093  *
22094  * TabPanel
22095  * 
22096  */
22097
22098 /**
22099  * @class Roo.bootstrap.TabPanel
22100  * @extends Roo.bootstrap.Component
22101  * Bootstrap TabPanel class
22102  * @cfg {Boolean} active panel active
22103  * @cfg {String} html panel content
22104  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22105  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22106  * @cfg {String} href click to link..
22107  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22108  * 
22109  * 
22110  * @constructor
22111  * Create a new TabPanel
22112  * @param {Object} config The config object
22113  */
22114
22115 Roo.bootstrap.TabPanel = function(config){
22116     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22117     this.addEvents({
22118         /**
22119              * @event changed
22120              * Fires when the active status changes
22121              * @param {Roo.bootstrap.TabPanel} this
22122              * @param {Boolean} state the new state
22123             
22124          */
22125         'changed': true,
22126         /**
22127              * @event beforedeactivate
22128              * Fires before a tab is de-activated - can be used to do validation on a form.
22129              * @param {Roo.bootstrap.TabPanel} this
22130              * @return {Boolean} false if there is an error
22131             
22132          */
22133         'beforedeactivate': true
22134      });
22135     
22136     this.tabId = this.tabId || Roo.id();
22137   
22138 };
22139
22140 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22141     
22142     active: false,
22143     html: false,
22144     tabId: false,
22145     navId : false,
22146     href : '',
22147     touchSlide : false,
22148     getAutoCreate : function(){
22149         
22150         
22151         var cfg = {
22152             tag: 'div',
22153             // item is needed for carousel - not sure if it has any effect otherwise
22154             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22155             html: this.html || ''
22156         };
22157         
22158         if(this.active){
22159             cfg.cls += ' active';
22160         }
22161         
22162         if(this.tabId){
22163             cfg.tabId = this.tabId;
22164         }
22165         
22166         
22167         
22168         return cfg;
22169     },
22170     
22171     initEvents:  function()
22172     {
22173         var p = this.parent();
22174         
22175         this.navId = this.navId || p.navId;
22176         
22177         if (typeof(this.navId) != 'undefined') {
22178             // not really needed.. but just in case.. parent should be a NavGroup.
22179             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22180             
22181             tg.register(this);
22182             
22183             var i = tg.tabs.length - 1;
22184             
22185             if(this.active && tg.bullets > 0 && i < tg.bullets){
22186                 tg.setActiveBullet(i);
22187             }
22188         }
22189         
22190         this.el.on('click', this.onClick, this);
22191         
22192         if(Roo.isTouch && this.touchSlide){
22193             this.el.on("touchstart", this.onTouchStart, this);
22194             this.el.on("touchmove", this.onTouchMove, this);
22195             this.el.on("touchend", this.onTouchEnd, this);
22196         }
22197         
22198     },
22199     
22200     onRender : function(ct, position)
22201     {
22202         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22203     },
22204     
22205     setActive : function(state)
22206     {
22207         Roo.log("panel - set active " + this.tabId + "=" + state);
22208         
22209         this.active = state;
22210         if (!state) {
22211             this.el.removeClass('active');
22212             
22213         } else  if (!this.el.hasClass('active')) {
22214             this.el.addClass('active');
22215         }
22216         
22217         this.fireEvent('changed', this, state);
22218     },
22219     
22220     onClick : function(e)
22221     {
22222         e.preventDefault();
22223         
22224         if(!this.href.length){
22225             return;
22226         }
22227         
22228         window.location.href = this.href;
22229     },
22230     
22231     startX : 0,
22232     startY : 0,
22233     endX : 0,
22234     endY : 0,
22235     swiping : false,
22236     
22237     onTouchStart : function(e)
22238     {
22239         this.swiping = false;
22240         
22241         this.startX = e.browserEvent.touches[0].clientX;
22242         this.startY = e.browserEvent.touches[0].clientY;
22243     },
22244     
22245     onTouchMove : function(e)
22246     {
22247         this.swiping = true;
22248         
22249         this.endX = e.browserEvent.touches[0].clientX;
22250         this.endY = e.browserEvent.touches[0].clientY;
22251     },
22252     
22253     onTouchEnd : function(e)
22254     {
22255         if(!this.swiping){
22256             this.onClick(e);
22257             return;
22258         }
22259         
22260         var tabGroup = this.parent();
22261         
22262         if(this.endX > this.startX){ // swiping right
22263             tabGroup.showPanelPrev();
22264             return;
22265         }
22266         
22267         if(this.startX > this.endX){ // swiping left
22268             tabGroup.showPanelNext();
22269             return;
22270         }
22271     }
22272     
22273     
22274 });
22275  
22276
22277  
22278
22279  /*
22280  * - LGPL
22281  *
22282  * DateField
22283  * 
22284  */
22285
22286 /**
22287  * @class Roo.bootstrap.DateField
22288  * @extends Roo.bootstrap.Input
22289  * Bootstrap DateField class
22290  * @cfg {Number} weekStart default 0
22291  * @cfg {String} viewMode default empty, (months|years)
22292  * @cfg {String} minViewMode default empty, (months|years)
22293  * @cfg {Number} startDate default -Infinity
22294  * @cfg {Number} endDate default Infinity
22295  * @cfg {Boolean} todayHighlight default false
22296  * @cfg {Boolean} todayBtn default false
22297  * @cfg {Boolean} calendarWeeks default false
22298  * @cfg {Object} daysOfWeekDisabled default empty
22299  * @cfg {Boolean} singleMode default false (true | false)
22300  * 
22301  * @cfg {Boolean} keyboardNavigation default true
22302  * @cfg {String} language default en
22303  * 
22304  * @constructor
22305  * Create a new DateField
22306  * @param {Object} config The config object
22307  */
22308
22309 Roo.bootstrap.DateField = function(config){
22310     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22311      this.addEvents({
22312             /**
22313              * @event show
22314              * Fires when this field show.
22315              * @param {Roo.bootstrap.DateField} this
22316              * @param {Mixed} date The date value
22317              */
22318             show : true,
22319             /**
22320              * @event show
22321              * Fires when this field hide.
22322              * @param {Roo.bootstrap.DateField} this
22323              * @param {Mixed} date The date value
22324              */
22325             hide : true,
22326             /**
22327              * @event select
22328              * Fires when select a date.
22329              * @param {Roo.bootstrap.DateField} this
22330              * @param {Mixed} date The date value
22331              */
22332             select : true,
22333             /**
22334              * @event beforeselect
22335              * Fires when before select a date.
22336              * @param {Roo.bootstrap.DateField} this
22337              * @param {Mixed} date The date value
22338              */
22339             beforeselect : true
22340         });
22341 };
22342
22343 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22344     
22345     /**
22346      * @cfg {String} format
22347      * The default date format string which can be overriden for localization support.  The format must be
22348      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22349      */
22350     format : "m/d/y",
22351     /**
22352      * @cfg {String} altFormats
22353      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22354      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22355      */
22356     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22357     
22358     weekStart : 0,
22359     
22360     viewMode : '',
22361     
22362     minViewMode : '',
22363     
22364     todayHighlight : false,
22365     
22366     todayBtn: false,
22367     
22368     language: 'en',
22369     
22370     keyboardNavigation: true,
22371     
22372     calendarWeeks: false,
22373     
22374     startDate: -Infinity,
22375     
22376     endDate: Infinity,
22377     
22378     daysOfWeekDisabled: [],
22379     
22380     _events: [],
22381     
22382     singleMode : false,
22383     
22384     UTCDate: function()
22385     {
22386         return new Date(Date.UTC.apply(Date, arguments));
22387     },
22388     
22389     UTCToday: function()
22390     {
22391         var today = new Date();
22392         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22393     },
22394     
22395     getDate: function() {
22396             var d = this.getUTCDate();
22397             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22398     },
22399     
22400     getUTCDate: function() {
22401             return this.date;
22402     },
22403     
22404     setDate: function(d) {
22405             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22406     },
22407     
22408     setUTCDate: function(d) {
22409             this.date = d;
22410             this.setValue(this.formatDate(this.date));
22411     },
22412         
22413     onRender: function(ct, position)
22414     {
22415         
22416         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22417         
22418         this.language = this.language || 'en';
22419         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22420         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22421         
22422         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22423         this.format = this.format || 'm/d/y';
22424         this.isInline = false;
22425         this.isInput = true;
22426         this.component = this.el.select('.add-on', true).first() || false;
22427         this.component = (this.component && this.component.length === 0) ? false : this.component;
22428         this.hasInput = this.component && this.inputEl().length;
22429         
22430         if (typeof(this.minViewMode === 'string')) {
22431             switch (this.minViewMode) {
22432                 case 'months':
22433                     this.minViewMode = 1;
22434                     break;
22435                 case 'years':
22436                     this.minViewMode = 2;
22437                     break;
22438                 default:
22439                     this.minViewMode = 0;
22440                     break;
22441             }
22442         }
22443         
22444         if (typeof(this.viewMode === 'string')) {
22445             switch (this.viewMode) {
22446                 case 'months':
22447                     this.viewMode = 1;
22448                     break;
22449                 case 'years':
22450                     this.viewMode = 2;
22451                     break;
22452                 default:
22453                     this.viewMode = 0;
22454                     break;
22455             }
22456         }
22457                 
22458         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22459         
22460 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22461         
22462         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22463         
22464         this.picker().on('mousedown', this.onMousedown, this);
22465         this.picker().on('click', this.onClick, this);
22466         
22467         this.picker().addClass('datepicker-dropdown');
22468         
22469         this.startViewMode = this.viewMode;
22470         
22471         if(this.singleMode){
22472             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22473                 v.setVisibilityMode(Roo.Element.DISPLAY);
22474                 v.hide();
22475             });
22476             
22477             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22478                 v.setStyle('width', '189px');
22479             });
22480         }
22481         
22482         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22483             if(!this.calendarWeeks){
22484                 v.remove();
22485                 return;
22486             }
22487             
22488             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22489             v.attr('colspan', function(i, val){
22490                 return parseInt(val) + 1;
22491             });
22492         });
22493                         
22494         
22495         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22496         
22497         this.setStartDate(this.startDate);
22498         this.setEndDate(this.endDate);
22499         
22500         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22501         
22502         this.fillDow();
22503         this.fillMonths();
22504         this.update();
22505         this.showMode();
22506         
22507         if(this.isInline) {
22508             this.showPopup();
22509         }
22510     },
22511     
22512     picker : function()
22513     {
22514         return this.pickerEl;
22515 //        return this.el.select('.datepicker', true).first();
22516     },
22517     
22518     fillDow: function()
22519     {
22520         var dowCnt = this.weekStart;
22521         
22522         var dow = {
22523             tag: 'tr',
22524             cn: [
22525                 
22526             ]
22527         };
22528         
22529         if(this.calendarWeeks){
22530             dow.cn.push({
22531                 tag: 'th',
22532                 cls: 'cw',
22533                 html: '&nbsp;'
22534             })
22535         }
22536         
22537         while (dowCnt < this.weekStart + 7) {
22538             dow.cn.push({
22539                 tag: 'th',
22540                 cls: 'dow',
22541                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22542             });
22543         }
22544         
22545         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22546     },
22547     
22548     fillMonths: function()
22549     {    
22550         var i = 0;
22551         var months = this.picker().select('>.datepicker-months td', true).first();
22552         
22553         months.dom.innerHTML = '';
22554         
22555         while (i < 12) {
22556             var month = {
22557                 tag: 'span',
22558                 cls: 'month',
22559                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22560             };
22561             
22562             months.createChild(month);
22563         }
22564         
22565     },
22566     
22567     update: function()
22568     {
22569         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;
22570         
22571         if (this.date < this.startDate) {
22572             this.viewDate = new Date(this.startDate);
22573         } else if (this.date > this.endDate) {
22574             this.viewDate = new Date(this.endDate);
22575         } else {
22576             this.viewDate = new Date(this.date);
22577         }
22578         
22579         this.fill();
22580     },
22581     
22582     fill: function() 
22583     {
22584         var d = new Date(this.viewDate),
22585                 year = d.getUTCFullYear(),
22586                 month = d.getUTCMonth(),
22587                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22588                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22589                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22590                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22591                 currentDate = this.date && this.date.valueOf(),
22592                 today = this.UTCToday();
22593         
22594         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22595         
22596 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22597         
22598 //        this.picker.select('>tfoot th.today').
22599 //                                              .text(dates[this.language].today)
22600 //                                              .toggle(this.todayBtn !== false);
22601     
22602         this.updateNavArrows();
22603         this.fillMonths();
22604                                                 
22605         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22606         
22607         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22608          
22609         prevMonth.setUTCDate(day);
22610         
22611         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22612         
22613         var nextMonth = new Date(prevMonth);
22614         
22615         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22616         
22617         nextMonth = nextMonth.valueOf();
22618         
22619         var fillMonths = false;
22620         
22621         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22622         
22623         while(prevMonth.valueOf() <= nextMonth) {
22624             var clsName = '';
22625             
22626             if (prevMonth.getUTCDay() === this.weekStart) {
22627                 if(fillMonths){
22628                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22629                 }
22630                     
22631                 fillMonths = {
22632                     tag: 'tr',
22633                     cn: []
22634                 };
22635                 
22636                 if(this.calendarWeeks){
22637                     // ISO 8601: First week contains first thursday.
22638                     // ISO also states week starts on Monday, but we can be more abstract here.
22639                     var
22640                     // Start of current week: based on weekstart/current date
22641                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22642                     // Thursday of this week
22643                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22644                     // First Thursday of year, year from thursday
22645                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22646                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22647                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22648                     
22649                     fillMonths.cn.push({
22650                         tag: 'td',
22651                         cls: 'cw',
22652                         html: calWeek
22653                     });
22654                 }
22655             }
22656             
22657             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22658                 clsName += ' old';
22659             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22660                 clsName += ' new';
22661             }
22662             if (this.todayHighlight &&
22663                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22664                 prevMonth.getUTCMonth() == today.getMonth() &&
22665                 prevMonth.getUTCDate() == today.getDate()) {
22666                 clsName += ' today';
22667             }
22668             
22669             if (currentDate && prevMonth.valueOf() === currentDate) {
22670                 clsName += ' active';
22671             }
22672             
22673             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22674                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22675                     clsName += ' disabled';
22676             }
22677             
22678             fillMonths.cn.push({
22679                 tag: 'td',
22680                 cls: 'day ' + clsName,
22681                 html: prevMonth.getDate()
22682             });
22683             
22684             prevMonth.setDate(prevMonth.getDate()+1);
22685         }
22686           
22687         var currentYear = this.date && this.date.getUTCFullYear();
22688         var currentMonth = this.date && this.date.getUTCMonth();
22689         
22690         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22691         
22692         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22693             v.removeClass('active');
22694             
22695             if(currentYear === year && k === currentMonth){
22696                 v.addClass('active');
22697             }
22698             
22699             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22700                 v.addClass('disabled');
22701             }
22702             
22703         });
22704         
22705         
22706         year = parseInt(year/10, 10) * 10;
22707         
22708         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22709         
22710         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22711         
22712         year -= 1;
22713         for (var i = -1; i < 11; i++) {
22714             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22715                 tag: 'span',
22716                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22717                 html: year
22718             });
22719             
22720             year += 1;
22721         }
22722     },
22723     
22724     showMode: function(dir) 
22725     {
22726         if (dir) {
22727             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22728         }
22729         
22730         Roo.each(this.picker().select('>div',true).elements, function(v){
22731             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22732             v.hide();
22733         });
22734         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22735     },
22736     
22737     place: function()
22738     {
22739         if(this.isInline) {
22740             return;
22741         }
22742         
22743         this.picker().removeClass(['bottom', 'top']);
22744         
22745         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22746             /*
22747              * place to the top of element!
22748              *
22749              */
22750             
22751             this.picker().addClass('top');
22752             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22753             
22754             return;
22755         }
22756         
22757         this.picker().addClass('bottom');
22758         
22759         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22760     },
22761     
22762     parseDate : function(value)
22763     {
22764         if(!value || value instanceof Date){
22765             return value;
22766         }
22767         var v = Date.parseDate(value, this.format);
22768         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22769             v = Date.parseDate(value, 'Y-m-d');
22770         }
22771         if(!v && this.altFormats){
22772             if(!this.altFormatsArray){
22773                 this.altFormatsArray = this.altFormats.split("|");
22774             }
22775             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22776                 v = Date.parseDate(value, this.altFormatsArray[i]);
22777             }
22778         }
22779         return v;
22780     },
22781     
22782     formatDate : function(date, fmt)
22783     {   
22784         return (!date || !(date instanceof Date)) ?
22785         date : date.dateFormat(fmt || this.format);
22786     },
22787     
22788     onFocus : function()
22789     {
22790         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22791         this.showPopup();
22792     },
22793     
22794     onBlur : function()
22795     {
22796         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22797         
22798         var d = this.inputEl().getValue();
22799         
22800         this.setValue(d);
22801                 
22802         this.hidePopup();
22803     },
22804     
22805     showPopup : function()
22806     {
22807         this.picker().show();
22808         this.update();
22809         this.place();
22810         
22811         this.fireEvent('showpopup', this, this.date);
22812     },
22813     
22814     hidePopup : function()
22815     {
22816         if(this.isInline) {
22817             return;
22818         }
22819         this.picker().hide();
22820         this.viewMode = this.startViewMode;
22821         this.showMode();
22822         
22823         this.fireEvent('hidepopup', this, this.date);
22824         
22825     },
22826     
22827     onMousedown: function(e)
22828     {
22829         e.stopPropagation();
22830         e.preventDefault();
22831     },
22832     
22833     keyup: function(e)
22834     {
22835         Roo.bootstrap.DateField.superclass.keyup.call(this);
22836         this.update();
22837     },
22838
22839     setValue: function(v)
22840     {
22841         if(this.fireEvent('beforeselect', this, v) !== false){
22842             var d = new Date(this.parseDate(v) ).clearTime();
22843         
22844             if(isNaN(d.getTime())){
22845                 this.date = this.viewDate = '';
22846                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22847                 return;
22848             }
22849
22850             v = this.formatDate(d);
22851
22852             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22853
22854             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22855
22856             this.update();
22857
22858             this.fireEvent('select', this, this.date);
22859         }
22860     },
22861     
22862     getValue: function()
22863     {
22864         return this.formatDate(this.date);
22865     },
22866     
22867     fireKey: function(e)
22868     {
22869         if (!this.picker().isVisible()){
22870             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22871                 this.showPopup();
22872             }
22873             return;
22874         }
22875         
22876         var dateChanged = false,
22877         dir, day, month,
22878         newDate, newViewDate;
22879         
22880         switch(e.keyCode){
22881             case 27: // escape
22882                 this.hidePopup();
22883                 e.preventDefault();
22884                 break;
22885             case 37: // left
22886             case 39: // right
22887                 if (!this.keyboardNavigation) {
22888                     break;
22889                 }
22890                 dir = e.keyCode == 37 ? -1 : 1;
22891                 
22892                 if (e.ctrlKey){
22893                     newDate = this.moveYear(this.date, dir);
22894                     newViewDate = this.moveYear(this.viewDate, dir);
22895                 } else if (e.shiftKey){
22896                     newDate = this.moveMonth(this.date, dir);
22897                     newViewDate = this.moveMonth(this.viewDate, dir);
22898                 } else {
22899                     newDate = new Date(this.date);
22900                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22901                     newViewDate = new Date(this.viewDate);
22902                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22903                 }
22904                 if (this.dateWithinRange(newDate)){
22905                     this.date = newDate;
22906                     this.viewDate = newViewDate;
22907                     this.setValue(this.formatDate(this.date));
22908 //                    this.update();
22909                     e.preventDefault();
22910                     dateChanged = true;
22911                 }
22912                 break;
22913             case 38: // up
22914             case 40: // down
22915                 if (!this.keyboardNavigation) {
22916                     break;
22917                 }
22918                 dir = e.keyCode == 38 ? -1 : 1;
22919                 if (e.ctrlKey){
22920                     newDate = this.moveYear(this.date, dir);
22921                     newViewDate = this.moveYear(this.viewDate, dir);
22922                 } else if (e.shiftKey){
22923                     newDate = this.moveMonth(this.date, dir);
22924                     newViewDate = this.moveMonth(this.viewDate, dir);
22925                 } else {
22926                     newDate = new Date(this.date);
22927                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22928                     newViewDate = new Date(this.viewDate);
22929                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22930                 }
22931                 if (this.dateWithinRange(newDate)){
22932                     this.date = newDate;
22933                     this.viewDate = newViewDate;
22934                     this.setValue(this.formatDate(this.date));
22935 //                    this.update();
22936                     e.preventDefault();
22937                     dateChanged = true;
22938                 }
22939                 break;
22940             case 13: // enter
22941                 this.setValue(this.formatDate(this.date));
22942                 this.hidePopup();
22943                 e.preventDefault();
22944                 break;
22945             case 9: // tab
22946                 this.setValue(this.formatDate(this.date));
22947                 this.hidePopup();
22948                 break;
22949             case 16: // shift
22950             case 17: // ctrl
22951             case 18: // alt
22952                 break;
22953             default :
22954                 this.hidePopup();
22955                 
22956         }
22957     },
22958     
22959     
22960     onClick: function(e) 
22961     {
22962         e.stopPropagation();
22963         e.preventDefault();
22964         
22965         var target = e.getTarget();
22966         
22967         if(target.nodeName.toLowerCase() === 'i'){
22968             target = Roo.get(target).dom.parentNode;
22969         }
22970         
22971         var nodeName = target.nodeName;
22972         var className = target.className;
22973         var html = target.innerHTML;
22974         //Roo.log(nodeName);
22975         
22976         switch(nodeName.toLowerCase()) {
22977             case 'th':
22978                 switch(className) {
22979                     case 'switch':
22980                         this.showMode(1);
22981                         break;
22982                     case 'prev':
22983                     case 'next':
22984                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22985                         switch(this.viewMode){
22986                                 case 0:
22987                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22988                                         break;
22989                                 case 1:
22990                                 case 2:
22991                                         this.viewDate = this.moveYear(this.viewDate, dir);
22992                                         break;
22993                         }
22994                         this.fill();
22995                         break;
22996                     case 'today':
22997                         var date = new Date();
22998                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22999 //                        this.fill()
23000                         this.setValue(this.formatDate(this.date));
23001                         
23002                         this.hidePopup();
23003                         break;
23004                 }
23005                 break;
23006             case 'span':
23007                 if (className.indexOf('disabled') < 0) {
23008                 if (!this.viewDate) {
23009                     this.viewDate = new Date();
23010                 }
23011                 this.viewDate.setUTCDate(1);
23012                     if (className.indexOf('month') > -1) {
23013                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23014                     } else {
23015                         var year = parseInt(html, 10) || 0;
23016                         this.viewDate.setUTCFullYear(year);
23017                         
23018                     }
23019                     
23020                     if(this.singleMode){
23021                         this.setValue(this.formatDate(this.viewDate));
23022                         this.hidePopup();
23023                         return;
23024                     }
23025                     
23026                     this.showMode(-1);
23027                     this.fill();
23028                 }
23029                 break;
23030                 
23031             case 'td':
23032                 //Roo.log(className);
23033                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23034                     var day = parseInt(html, 10) || 1;
23035                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23036                         month = (this.viewDate || new Date()).getUTCMonth();
23037
23038                     if (className.indexOf('old') > -1) {
23039                         if(month === 0 ){
23040                             month = 11;
23041                             year -= 1;
23042                         }else{
23043                             month -= 1;
23044                         }
23045                     } else if (className.indexOf('new') > -1) {
23046                         if (month == 11) {
23047                             month = 0;
23048                             year += 1;
23049                         } else {
23050                             month += 1;
23051                         }
23052                     }
23053                     //Roo.log([year,month,day]);
23054                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23055                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23056 //                    this.fill();
23057                     //Roo.log(this.formatDate(this.date));
23058                     this.setValue(this.formatDate(this.date));
23059                     this.hidePopup();
23060                 }
23061                 break;
23062         }
23063     },
23064     
23065     setStartDate: function(startDate)
23066     {
23067         this.startDate = startDate || -Infinity;
23068         if (this.startDate !== -Infinity) {
23069             this.startDate = this.parseDate(this.startDate);
23070         }
23071         this.update();
23072         this.updateNavArrows();
23073     },
23074
23075     setEndDate: function(endDate)
23076     {
23077         this.endDate = endDate || Infinity;
23078         if (this.endDate !== Infinity) {
23079             this.endDate = this.parseDate(this.endDate);
23080         }
23081         this.update();
23082         this.updateNavArrows();
23083     },
23084     
23085     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23086     {
23087         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23088         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23089             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23090         }
23091         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23092             return parseInt(d, 10);
23093         });
23094         this.update();
23095         this.updateNavArrows();
23096     },
23097     
23098     updateNavArrows: function() 
23099     {
23100         if(this.singleMode){
23101             return;
23102         }
23103         
23104         var d = new Date(this.viewDate),
23105         year = d.getUTCFullYear(),
23106         month = d.getUTCMonth();
23107         
23108         Roo.each(this.picker().select('.prev', true).elements, function(v){
23109             v.show();
23110             switch (this.viewMode) {
23111                 case 0:
23112
23113                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23114                         v.hide();
23115                     }
23116                     break;
23117                 case 1:
23118                 case 2:
23119                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23120                         v.hide();
23121                     }
23122                     break;
23123             }
23124         });
23125         
23126         Roo.each(this.picker().select('.next', true).elements, function(v){
23127             v.show();
23128             switch (this.viewMode) {
23129                 case 0:
23130
23131                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23132                         v.hide();
23133                     }
23134                     break;
23135                 case 1:
23136                 case 2:
23137                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23138                         v.hide();
23139                     }
23140                     break;
23141             }
23142         })
23143     },
23144     
23145     moveMonth: function(date, dir)
23146     {
23147         if (!dir) {
23148             return date;
23149         }
23150         var new_date = new Date(date.valueOf()),
23151         day = new_date.getUTCDate(),
23152         month = new_date.getUTCMonth(),
23153         mag = Math.abs(dir),
23154         new_month, test;
23155         dir = dir > 0 ? 1 : -1;
23156         if (mag == 1){
23157             test = dir == -1
23158             // If going back one month, make sure month is not current month
23159             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23160             ? function(){
23161                 return new_date.getUTCMonth() == month;
23162             }
23163             // If going forward one month, make sure month is as expected
23164             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23165             : function(){
23166                 return new_date.getUTCMonth() != new_month;
23167             };
23168             new_month = month + dir;
23169             new_date.setUTCMonth(new_month);
23170             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23171             if (new_month < 0 || new_month > 11) {
23172                 new_month = (new_month + 12) % 12;
23173             }
23174         } else {
23175             // For magnitudes >1, move one month at a time...
23176             for (var i=0; i<mag; i++) {
23177                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23178                 new_date = this.moveMonth(new_date, dir);
23179             }
23180             // ...then reset the day, keeping it in the new month
23181             new_month = new_date.getUTCMonth();
23182             new_date.setUTCDate(day);
23183             test = function(){
23184                 return new_month != new_date.getUTCMonth();
23185             };
23186         }
23187         // Common date-resetting loop -- if date is beyond end of month, make it
23188         // end of month
23189         while (test()){
23190             new_date.setUTCDate(--day);
23191             new_date.setUTCMonth(new_month);
23192         }
23193         return new_date;
23194     },
23195
23196     moveYear: function(date, dir)
23197     {
23198         return this.moveMonth(date, dir*12);
23199     },
23200
23201     dateWithinRange: function(date)
23202     {
23203         return date >= this.startDate && date <= this.endDate;
23204     },
23205
23206     
23207     remove: function() 
23208     {
23209         this.picker().remove();
23210     },
23211     
23212     validateValue : function(value)
23213     {
23214         if(this.getVisibilityEl().hasClass('hidden')){
23215             return true;
23216         }
23217         
23218         if(value.length < 1)  {
23219             if(this.allowBlank){
23220                 return true;
23221             }
23222             return false;
23223         }
23224         
23225         if(value.length < this.minLength){
23226             return false;
23227         }
23228         if(value.length > this.maxLength){
23229             return false;
23230         }
23231         if(this.vtype){
23232             var vt = Roo.form.VTypes;
23233             if(!vt[this.vtype](value, this)){
23234                 return false;
23235             }
23236         }
23237         if(typeof this.validator == "function"){
23238             var msg = this.validator(value);
23239             if(msg !== true){
23240                 return false;
23241             }
23242         }
23243         
23244         if(this.regex && !this.regex.test(value)){
23245             return false;
23246         }
23247         
23248         if(typeof(this.parseDate(value)) == 'undefined'){
23249             return false;
23250         }
23251         
23252         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23253             return false;
23254         }      
23255         
23256         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23257             return false;
23258         } 
23259         
23260         
23261         return true;
23262     },
23263     
23264     reset : function()
23265     {
23266         this.date = this.viewDate = '';
23267         
23268         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23269     }
23270    
23271 });
23272
23273 Roo.apply(Roo.bootstrap.DateField,  {
23274     
23275     head : {
23276         tag: 'thead',
23277         cn: [
23278         {
23279             tag: 'tr',
23280             cn: [
23281             {
23282                 tag: 'th',
23283                 cls: 'prev',
23284                 html: '<i class="fa fa-arrow-left"/>'
23285             },
23286             {
23287                 tag: 'th',
23288                 cls: 'switch',
23289                 colspan: '5'
23290             },
23291             {
23292                 tag: 'th',
23293                 cls: 'next',
23294                 html: '<i class="fa fa-arrow-right"/>'
23295             }
23296
23297             ]
23298         }
23299         ]
23300     },
23301     
23302     content : {
23303         tag: 'tbody',
23304         cn: [
23305         {
23306             tag: 'tr',
23307             cn: [
23308             {
23309                 tag: 'td',
23310                 colspan: '7'
23311             }
23312             ]
23313         }
23314         ]
23315     },
23316     
23317     footer : {
23318         tag: 'tfoot',
23319         cn: [
23320         {
23321             tag: 'tr',
23322             cn: [
23323             {
23324                 tag: 'th',
23325                 colspan: '7',
23326                 cls: 'today'
23327             }
23328                     
23329             ]
23330         }
23331         ]
23332     },
23333     
23334     dates:{
23335         en: {
23336             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23337             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23338             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23339             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23340             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23341             today: "Today"
23342         }
23343     },
23344     
23345     modes: [
23346     {
23347         clsName: 'days',
23348         navFnc: 'Month',
23349         navStep: 1
23350     },
23351     {
23352         clsName: 'months',
23353         navFnc: 'FullYear',
23354         navStep: 1
23355     },
23356     {
23357         clsName: 'years',
23358         navFnc: 'FullYear',
23359         navStep: 10
23360     }]
23361 });
23362
23363 Roo.apply(Roo.bootstrap.DateField,  {
23364   
23365     template : {
23366         tag: 'div',
23367         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23368         cn: [
23369         {
23370             tag: 'div',
23371             cls: 'datepicker-days',
23372             cn: [
23373             {
23374                 tag: 'table',
23375                 cls: 'table-condensed',
23376                 cn:[
23377                 Roo.bootstrap.DateField.head,
23378                 {
23379                     tag: 'tbody'
23380                 },
23381                 Roo.bootstrap.DateField.footer
23382                 ]
23383             }
23384             ]
23385         },
23386         {
23387             tag: 'div',
23388             cls: 'datepicker-months',
23389             cn: [
23390             {
23391                 tag: 'table',
23392                 cls: 'table-condensed',
23393                 cn:[
23394                 Roo.bootstrap.DateField.head,
23395                 Roo.bootstrap.DateField.content,
23396                 Roo.bootstrap.DateField.footer
23397                 ]
23398             }
23399             ]
23400         },
23401         {
23402             tag: 'div',
23403             cls: 'datepicker-years',
23404             cn: [
23405             {
23406                 tag: 'table',
23407                 cls: 'table-condensed',
23408                 cn:[
23409                 Roo.bootstrap.DateField.head,
23410                 Roo.bootstrap.DateField.content,
23411                 Roo.bootstrap.DateField.footer
23412                 ]
23413             }
23414             ]
23415         }
23416         ]
23417     }
23418 });
23419
23420  
23421
23422  /*
23423  * - LGPL
23424  *
23425  * TimeField
23426  * 
23427  */
23428
23429 /**
23430  * @class Roo.bootstrap.TimeField
23431  * @extends Roo.bootstrap.Input
23432  * Bootstrap DateField class
23433  * 
23434  * 
23435  * @constructor
23436  * Create a new TimeField
23437  * @param {Object} config The config object
23438  */
23439
23440 Roo.bootstrap.TimeField = function(config){
23441     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23442     this.addEvents({
23443             /**
23444              * @event show
23445              * Fires when this field show.
23446              * @param {Roo.bootstrap.DateField} thisthis
23447              * @param {Mixed} date The date value
23448              */
23449             show : true,
23450             /**
23451              * @event show
23452              * Fires when this field hide.
23453              * @param {Roo.bootstrap.DateField} this
23454              * @param {Mixed} date The date value
23455              */
23456             hide : true,
23457             /**
23458              * @event select
23459              * Fires when select a date.
23460              * @param {Roo.bootstrap.DateField} this
23461              * @param {Mixed} date The date value
23462              */
23463             select : true
23464         });
23465 };
23466
23467 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23468     
23469     /**
23470      * @cfg {String} format
23471      * The default time format string which can be overriden for localization support.  The format must be
23472      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23473      */
23474     format : "H:i",
23475
23476     getAutoCreate : function()
23477     {
23478         this.after = '<i class="fa far fa-clock"></i>';
23479         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23480         
23481          
23482     },
23483     onRender: function(ct, position)
23484     {
23485         
23486         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23487                 
23488         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23489         
23490         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23491         
23492         this.pop = this.picker().select('>.datepicker-time',true).first();
23493         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23494         
23495         this.picker().on('mousedown', this.onMousedown, this);
23496         this.picker().on('click', this.onClick, this);
23497         
23498         this.picker().addClass('datepicker-dropdown');
23499     
23500         this.fillTime();
23501         this.update();
23502             
23503         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23504         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23505         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23506         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23507         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23508         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23509
23510     },
23511     
23512     fireKey: function(e){
23513         if (!this.picker().isVisible()){
23514             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23515                 this.show();
23516             }
23517             return;
23518         }
23519
23520         e.preventDefault();
23521         
23522         switch(e.keyCode){
23523             case 27: // escape
23524                 this.hide();
23525                 break;
23526             case 37: // left
23527             case 39: // right
23528                 this.onTogglePeriod();
23529                 break;
23530             case 38: // up
23531                 this.onIncrementMinutes();
23532                 break;
23533             case 40: // down
23534                 this.onDecrementMinutes();
23535                 break;
23536             case 13: // enter
23537             case 9: // tab
23538                 this.setTime();
23539                 break;
23540         }
23541     },
23542     
23543     onClick: function(e) {
23544         e.stopPropagation();
23545         e.preventDefault();
23546     },
23547     
23548     picker : function()
23549     {
23550         return this.pickerEl;
23551     },
23552     
23553     fillTime: function()
23554     {    
23555         var time = this.pop.select('tbody', true).first();
23556         
23557         time.dom.innerHTML = '';
23558         
23559         time.createChild({
23560             tag: 'tr',
23561             cn: [
23562                 {
23563                     tag: 'td',
23564                     cn: [
23565                         {
23566                             tag: 'a',
23567                             href: '#',
23568                             cls: 'btn',
23569                             cn: [
23570                                 {
23571                                     tag: 'i',
23572                                     cls: 'hours-up fa fas fa-chevron-up'
23573                                 }
23574                             ]
23575                         } 
23576                     ]
23577                 },
23578                 {
23579                     tag: 'td',
23580                     cls: 'separator'
23581                 },
23582                 {
23583                     tag: 'td',
23584                     cn: [
23585                         {
23586                             tag: 'a',
23587                             href: '#',
23588                             cls: 'btn',
23589                             cn: [
23590                                 {
23591                                     tag: 'i',
23592                                     cls: 'minutes-up fa fas fa-chevron-up'
23593                                 }
23594                             ]
23595                         }
23596                     ]
23597                 },
23598                 {
23599                     tag: 'td',
23600                     cls: 'separator'
23601                 }
23602             ]
23603         });
23604         
23605         time.createChild({
23606             tag: 'tr',
23607             cn: [
23608                 {
23609                     tag: 'td',
23610                     cn: [
23611                         {
23612                             tag: 'span',
23613                             cls: 'timepicker-hour',
23614                             html: '00'
23615                         }  
23616                     ]
23617                 },
23618                 {
23619                     tag: 'td',
23620                     cls: 'separator',
23621                     html: ':'
23622                 },
23623                 {
23624                     tag: 'td',
23625                     cn: [
23626                         {
23627                             tag: 'span',
23628                             cls: 'timepicker-minute',
23629                             html: '00'
23630                         }  
23631                     ]
23632                 },
23633                 {
23634                     tag: 'td',
23635                     cls: 'separator'
23636                 },
23637                 {
23638                     tag: 'td',
23639                     cn: [
23640                         {
23641                             tag: 'button',
23642                             type: 'button',
23643                             cls: 'btn btn-primary period',
23644                             html: 'AM'
23645                             
23646                         }
23647                     ]
23648                 }
23649             ]
23650         });
23651         
23652         time.createChild({
23653             tag: 'tr',
23654             cn: [
23655                 {
23656                     tag: 'td',
23657                     cn: [
23658                         {
23659                             tag: 'a',
23660                             href: '#',
23661                             cls: 'btn',
23662                             cn: [
23663                                 {
23664                                     tag: 'span',
23665                                     cls: 'hours-down fa fas fa-chevron-down'
23666                                 }
23667                             ]
23668                         }
23669                     ]
23670                 },
23671                 {
23672                     tag: 'td',
23673                     cls: 'separator'
23674                 },
23675                 {
23676                     tag: 'td',
23677                     cn: [
23678                         {
23679                             tag: 'a',
23680                             href: '#',
23681                             cls: 'btn',
23682                             cn: [
23683                                 {
23684                                     tag: 'span',
23685                                     cls: 'minutes-down fa fas fa-chevron-down'
23686                                 }
23687                             ]
23688                         }
23689                     ]
23690                 },
23691                 {
23692                     tag: 'td',
23693                     cls: 'separator'
23694                 }
23695             ]
23696         });
23697         
23698     },
23699     
23700     update: function()
23701     {
23702         
23703         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23704         
23705         this.fill();
23706     },
23707     
23708     fill: function() 
23709     {
23710         var hours = this.time.getHours();
23711         var minutes = this.time.getMinutes();
23712         var period = 'AM';
23713         
23714         if(hours > 11){
23715             period = 'PM';
23716         }
23717         
23718         if(hours == 0){
23719             hours = 12;
23720         }
23721         
23722         
23723         if(hours > 12){
23724             hours = hours - 12;
23725         }
23726         
23727         if(hours < 10){
23728             hours = '0' + hours;
23729         }
23730         
23731         if(minutes < 10){
23732             minutes = '0' + minutes;
23733         }
23734         
23735         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23736         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23737         this.pop.select('button', true).first().dom.innerHTML = period;
23738         
23739     },
23740     
23741     place: function()
23742     {   
23743         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23744         
23745         var cls = ['bottom'];
23746         
23747         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23748             cls.pop();
23749             cls.push('top');
23750         }
23751         
23752         cls.push('right');
23753         
23754         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23755             cls.pop();
23756             cls.push('left');
23757         }
23758         //this.picker().setXY(20000,20000);
23759         this.picker().addClass(cls.join('-'));
23760         
23761         var _this = this;
23762         
23763         Roo.each(cls, function(c){
23764             if(c == 'bottom'){
23765                 (function() {
23766                  //  
23767                 }).defer(200);
23768                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23769                 //_this.picker().setTop(_this.inputEl().getHeight());
23770                 return;
23771             }
23772             if(c == 'top'){
23773                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23774                 
23775                 //_this.picker().setTop(0 - _this.picker().getHeight());
23776                 return;
23777             }
23778             /*
23779             if(c == 'left'){
23780                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23781                 return;
23782             }
23783             if(c == 'right'){
23784                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23785                 return;
23786             }
23787             */
23788         });
23789         
23790     },
23791   
23792     onFocus : function()
23793     {
23794         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23795         this.show();
23796     },
23797     
23798     onBlur : function()
23799     {
23800         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23801         this.hide();
23802     },
23803     
23804     show : function()
23805     {
23806         this.picker().show();
23807         this.pop.show();
23808         this.update();
23809         this.place();
23810         
23811         this.fireEvent('show', this, this.date);
23812     },
23813     
23814     hide : function()
23815     {
23816         this.picker().hide();
23817         this.pop.hide();
23818         
23819         this.fireEvent('hide', this, this.date);
23820     },
23821     
23822     setTime : function()
23823     {
23824         this.hide();
23825         this.setValue(this.time.format(this.format));
23826         
23827         this.fireEvent('select', this, this.date);
23828         
23829         
23830     },
23831     
23832     onMousedown: function(e){
23833         e.stopPropagation();
23834         e.preventDefault();
23835     },
23836     
23837     onIncrementHours: function()
23838     {
23839         Roo.log('onIncrementHours');
23840         this.time = this.time.add(Date.HOUR, 1);
23841         this.update();
23842         
23843     },
23844     
23845     onDecrementHours: function()
23846     {
23847         Roo.log('onDecrementHours');
23848         this.time = this.time.add(Date.HOUR, -1);
23849         this.update();
23850     },
23851     
23852     onIncrementMinutes: function()
23853     {
23854         Roo.log('onIncrementMinutes');
23855         this.time = this.time.add(Date.MINUTE, 1);
23856         this.update();
23857     },
23858     
23859     onDecrementMinutes: function()
23860     {
23861         Roo.log('onDecrementMinutes');
23862         this.time = this.time.add(Date.MINUTE, -1);
23863         this.update();
23864     },
23865     
23866     onTogglePeriod: function()
23867     {
23868         Roo.log('onTogglePeriod');
23869         this.time = this.time.add(Date.HOUR, 12);
23870         this.update();
23871     }
23872     
23873    
23874 });
23875  
23876
23877 Roo.apply(Roo.bootstrap.TimeField,  {
23878   
23879     template : {
23880         tag: 'div',
23881         cls: 'datepicker dropdown-menu',
23882         cn: [
23883             {
23884                 tag: 'div',
23885                 cls: 'datepicker-time',
23886                 cn: [
23887                 {
23888                     tag: 'table',
23889                     cls: 'table-condensed',
23890                     cn:[
23891                         {
23892                             tag: 'tbody',
23893                             cn: [
23894                                 {
23895                                     tag: 'tr',
23896                                     cn: [
23897                                     {
23898                                         tag: 'td',
23899                                         colspan: '7'
23900                                     }
23901                                     ]
23902                                 }
23903                             ]
23904                         },
23905                         {
23906                             tag: 'tfoot',
23907                             cn: [
23908                                 {
23909                                     tag: 'tr',
23910                                     cn: [
23911                                     {
23912                                         tag: 'th',
23913                                         colspan: '7',
23914                                         cls: '',
23915                                         cn: [
23916                                             {
23917                                                 tag: 'button',
23918                                                 cls: 'btn btn-info ok',
23919                                                 html: 'OK'
23920                                             }
23921                                         ]
23922                                     }
23923                     
23924                                     ]
23925                                 }
23926                             ]
23927                         }
23928                     ]
23929                 }
23930                 ]
23931             }
23932         ]
23933     }
23934 });
23935
23936  
23937
23938  /*
23939  * - LGPL
23940  *
23941  * MonthField
23942  * 
23943  */
23944
23945 /**
23946  * @class Roo.bootstrap.MonthField
23947  * @extends Roo.bootstrap.Input
23948  * Bootstrap MonthField class
23949  * 
23950  * @cfg {String} language default en
23951  * 
23952  * @constructor
23953  * Create a new MonthField
23954  * @param {Object} config The config object
23955  */
23956
23957 Roo.bootstrap.MonthField = function(config){
23958     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23959     
23960     this.addEvents({
23961         /**
23962          * @event show
23963          * Fires when this field show.
23964          * @param {Roo.bootstrap.MonthField} this
23965          * @param {Mixed} date The date value
23966          */
23967         show : true,
23968         /**
23969          * @event show
23970          * Fires when this field hide.
23971          * @param {Roo.bootstrap.MonthField} this
23972          * @param {Mixed} date The date value
23973          */
23974         hide : true,
23975         /**
23976          * @event select
23977          * Fires when select a date.
23978          * @param {Roo.bootstrap.MonthField} this
23979          * @param {String} oldvalue The old value
23980          * @param {String} newvalue The new value
23981          */
23982         select : true
23983     });
23984 };
23985
23986 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23987     
23988     onRender: function(ct, position)
23989     {
23990         
23991         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23992         
23993         this.language = this.language || 'en';
23994         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23995         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23996         
23997         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23998         this.isInline = false;
23999         this.isInput = true;
24000         this.component = this.el.select('.add-on', true).first() || false;
24001         this.component = (this.component && this.component.length === 0) ? false : this.component;
24002         this.hasInput = this.component && this.inputEL().length;
24003         
24004         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24005         
24006         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24007         
24008         this.picker().on('mousedown', this.onMousedown, this);
24009         this.picker().on('click', this.onClick, this);
24010         
24011         this.picker().addClass('datepicker-dropdown');
24012         
24013         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24014             v.setStyle('width', '189px');
24015         });
24016         
24017         this.fillMonths();
24018         
24019         this.update();
24020         
24021         if(this.isInline) {
24022             this.show();
24023         }
24024         
24025     },
24026     
24027     setValue: function(v, suppressEvent)
24028     {   
24029         var o = this.getValue();
24030         
24031         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24032         
24033         this.update();
24034
24035         if(suppressEvent !== true){
24036             this.fireEvent('select', this, o, v);
24037         }
24038         
24039     },
24040     
24041     getValue: function()
24042     {
24043         return this.value;
24044     },
24045     
24046     onClick: function(e) 
24047     {
24048         e.stopPropagation();
24049         e.preventDefault();
24050         
24051         var target = e.getTarget();
24052         
24053         if(target.nodeName.toLowerCase() === 'i'){
24054             target = Roo.get(target).dom.parentNode;
24055         }
24056         
24057         var nodeName = target.nodeName;
24058         var className = target.className;
24059         var html = target.innerHTML;
24060         
24061         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24062             return;
24063         }
24064         
24065         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24066         
24067         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24068         
24069         this.hide();
24070                         
24071     },
24072     
24073     picker : function()
24074     {
24075         return this.pickerEl;
24076     },
24077     
24078     fillMonths: function()
24079     {    
24080         var i = 0;
24081         var months = this.picker().select('>.datepicker-months td', true).first();
24082         
24083         months.dom.innerHTML = '';
24084         
24085         while (i < 12) {
24086             var month = {
24087                 tag: 'span',
24088                 cls: 'month',
24089                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24090             };
24091             
24092             months.createChild(month);
24093         }
24094         
24095     },
24096     
24097     update: function()
24098     {
24099         var _this = this;
24100         
24101         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24102             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24103         }
24104         
24105         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24106             e.removeClass('active');
24107             
24108             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24109                 e.addClass('active');
24110             }
24111         })
24112     },
24113     
24114     place: function()
24115     {
24116         if(this.isInline) {
24117             return;
24118         }
24119         
24120         this.picker().removeClass(['bottom', 'top']);
24121         
24122         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24123             /*
24124              * place to the top of element!
24125              *
24126              */
24127             
24128             this.picker().addClass('top');
24129             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24130             
24131             return;
24132         }
24133         
24134         this.picker().addClass('bottom');
24135         
24136         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24137     },
24138     
24139     onFocus : function()
24140     {
24141         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24142         this.show();
24143     },
24144     
24145     onBlur : function()
24146     {
24147         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24148         
24149         var d = this.inputEl().getValue();
24150         
24151         this.setValue(d);
24152                 
24153         this.hide();
24154     },
24155     
24156     show : function()
24157     {
24158         this.picker().show();
24159         this.picker().select('>.datepicker-months', true).first().show();
24160         this.update();
24161         this.place();
24162         
24163         this.fireEvent('show', this, this.date);
24164     },
24165     
24166     hide : function()
24167     {
24168         if(this.isInline) {
24169             return;
24170         }
24171         this.picker().hide();
24172         this.fireEvent('hide', this, this.date);
24173         
24174     },
24175     
24176     onMousedown: function(e)
24177     {
24178         e.stopPropagation();
24179         e.preventDefault();
24180     },
24181     
24182     keyup: function(e)
24183     {
24184         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24185         this.update();
24186     },
24187
24188     fireKey: function(e)
24189     {
24190         if (!this.picker().isVisible()){
24191             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24192                 this.show();
24193             }
24194             return;
24195         }
24196         
24197         var dir;
24198         
24199         switch(e.keyCode){
24200             case 27: // escape
24201                 this.hide();
24202                 e.preventDefault();
24203                 break;
24204             case 37: // left
24205             case 39: // right
24206                 dir = e.keyCode == 37 ? -1 : 1;
24207                 
24208                 this.vIndex = this.vIndex + dir;
24209                 
24210                 if(this.vIndex < 0){
24211                     this.vIndex = 0;
24212                 }
24213                 
24214                 if(this.vIndex > 11){
24215                     this.vIndex = 11;
24216                 }
24217                 
24218                 if(isNaN(this.vIndex)){
24219                     this.vIndex = 0;
24220                 }
24221                 
24222                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24223                 
24224                 break;
24225             case 38: // up
24226             case 40: // down
24227                 
24228                 dir = e.keyCode == 38 ? -1 : 1;
24229                 
24230                 this.vIndex = this.vIndex + dir * 4;
24231                 
24232                 if(this.vIndex < 0){
24233                     this.vIndex = 0;
24234                 }
24235                 
24236                 if(this.vIndex > 11){
24237                     this.vIndex = 11;
24238                 }
24239                 
24240                 if(isNaN(this.vIndex)){
24241                     this.vIndex = 0;
24242                 }
24243                 
24244                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24245                 break;
24246                 
24247             case 13: // enter
24248                 
24249                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24250                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24251                 }
24252                 
24253                 this.hide();
24254                 e.preventDefault();
24255                 break;
24256             case 9: // tab
24257                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24258                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24259                 }
24260                 this.hide();
24261                 break;
24262             case 16: // shift
24263             case 17: // ctrl
24264             case 18: // alt
24265                 break;
24266             default :
24267                 this.hide();
24268                 
24269         }
24270     },
24271     
24272     remove: function() 
24273     {
24274         this.picker().remove();
24275     }
24276    
24277 });
24278
24279 Roo.apply(Roo.bootstrap.MonthField,  {
24280     
24281     content : {
24282         tag: 'tbody',
24283         cn: [
24284         {
24285             tag: 'tr',
24286             cn: [
24287             {
24288                 tag: 'td',
24289                 colspan: '7'
24290             }
24291             ]
24292         }
24293         ]
24294     },
24295     
24296     dates:{
24297         en: {
24298             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24299             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24300         }
24301     }
24302 });
24303
24304 Roo.apply(Roo.bootstrap.MonthField,  {
24305   
24306     template : {
24307         tag: 'div',
24308         cls: 'datepicker dropdown-menu roo-dynamic',
24309         cn: [
24310             {
24311                 tag: 'div',
24312                 cls: 'datepicker-months',
24313                 cn: [
24314                 {
24315                     tag: 'table',
24316                     cls: 'table-condensed',
24317                     cn:[
24318                         Roo.bootstrap.DateField.content
24319                     ]
24320                 }
24321                 ]
24322             }
24323         ]
24324     }
24325 });
24326
24327  
24328
24329  
24330  /*
24331  * - LGPL
24332  *
24333  * CheckBox
24334  * 
24335  */
24336
24337 /**
24338  * @class Roo.bootstrap.CheckBox
24339  * @extends Roo.bootstrap.Input
24340  * Bootstrap CheckBox class
24341  * 
24342  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24343  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24344  * @cfg {String} boxLabel The text that appears beside the checkbox
24345  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24346  * @cfg {Boolean} checked initnal the element
24347  * @cfg {Boolean} inline inline the element (default false)
24348  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24349  * @cfg {String} tooltip label tooltip
24350  * 
24351  * @constructor
24352  * Create a new CheckBox
24353  * @param {Object} config The config object
24354  */
24355
24356 Roo.bootstrap.CheckBox = function(config){
24357     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24358    
24359     this.addEvents({
24360         /**
24361         * @event check
24362         * Fires when the element is checked or unchecked.
24363         * @param {Roo.bootstrap.CheckBox} this This input
24364         * @param {Boolean} checked The new checked value
24365         */
24366        check : true,
24367        /**
24368         * @event click
24369         * Fires when the element is click.
24370         * @param {Roo.bootstrap.CheckBox} this This input
24371         */
24372        click : true
24373     });
24374     
24375 };
24376
24377 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24378   
24379     inputType: 'checkbox',
24380     inputValue: 1,
24381     valueOff: 0,
24382     boxLabel: false,
24383     checked: false,
24384     weight : false,
24385     inline: false,
24386     tooltip : '',
24387     
24388     // checkbox success does not make any sense really.. 
24389     invalidClass : "",
24390     validClass : "",
24391     
24392     
24393     getAutoCreate : function()
24394     {
24395         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24396         
24397         var id = Roo.id();
24398         
24399         var cfg = {};
24400         
24401         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24402         
24403         if(this.inline){
24404             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24405         }
24406         
24407         var input =  {
24408             tag: 'input',
24409             id : id,
24410             type : this.inputType,
24411             value : this.inputValue,
24412             cls : 'roo-' + this.inputType, //'form-box',
24413             placeholder : this.placeholder || ''
24414             
24415         };
24416         
24417         if(this.inputType != 'radio'){
24418             var hidden =  {
24419                 tag: 'input',
24420                 type : 'hidden',
24421                 cls : 'roo-hidden-value',
24422                 value : this.checked ? this.inputValue : this.valueOff
24423             };
24424         }
24425         
24426             
24427         if (this.weight) { // Validity check?
24428             cfg.cls += " " + this.inputType + "-" + this.weight;
24429         }
24430         
24431         if (this.disabled) {
24432             input.disabled=true;
24433         }
24434         
24435         if(this.checked){
24436             input.checked = this.checked;
24437         }
24438         
24439         if (this.name) {
24440             
24441             input.name = this.name;
24442             
24443             if(this.inputType != 'radio'){
24444                 hidden.name = this.name;
24445                 input.name = '_hidden_' + this.name;
24446             }
24447         }
24448         
24449         if (this.size) {
24450             input.cls += ' input-' + this.size;
24451         }
24452         
24453         var settings=this;
24454         
24455         ['xs','sm','md','lg'].map(function(size){
24456             if (settings[size]) {
24457                 cfg.cls += ' col-' + size + '-' + settings[size];
24458             }
24459         });
24460         
24461         var inputblock = input;
24462          
24463         if (this.before || this.after) {
24464             
24465             inputblock = {
24466                 cls : 'input-group',
24467                 cn :  [] 
24468             };
24469             
24470             if (this.before) {
24471                 inputblock.cn.push({
24472                     tag :'span',
24473                     cls : 'input-group-addon',
24474                     html : this.before
24475                 });
24476             }
24477             
24478             inputblock.cn.push(input);
24479             
24480             if(this.inputType != 'radio'){
24481                 inputblock.cn.push(hidden);
24482             }
24483             
24484             if (this.after) {
24485                 inputblock.cn.push({
24486                     tag :'span',
24487                     cls : 'input-group-addon',
24488                     html : this.after
24489                 });
24490             }
24491             
24492         }
24493         var boxLabelCfg = false;
24494         
24495         if(this.boxLabel){
24496            
24497             boxLabelCfg = {
24498                 tag: 'label',
24499                 //'for': id, // box label is handled by onclick - so no for...
24500                 cls: 'box-label',
24501                 html: this.boxLabel
24502             };
24503             if(this.tooltip){
24504                 boxLabelCfg.tooltip = this.tooltip;
24505             }
24506              
24507         }
24508         
24509         
24510         if (align ==='left' && this.fieldLabel.length) {
24511 //                Roo.log("left and has label");
24512             cfg.cn = [
24513                 {
24514                     tag: 'label',
24515                     'for' :  id,
24516                     cls : 'control-label',
24517                     html : this.fieldLabel
24518                 },
24519                 {
24520                     cls : "", 
24521                     cn: [
24522                         inputblock
24523                     ]
24524                 }
24525             ];
24526             
24527             if (boxLabelCfg) {
24528                 cfg.cn[1].cn.push(boxLabelCfg);
24529             }
24530             
24531             if(this.labelWidth > 12){
24532                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24533             }
24534             
24535             if(this.labelWidth < 13 && this.labelmd == 0){
24536                 this.labelmd = this.labelWidth;
24537             }
24538             
24539             if(this.labellg > 0){
24540                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24541                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24542             }
24543             
24544             if(this.labelmd > 0){
24545                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24546                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24547             }
24548             
24549             if(this.labelsm > 0){
24550                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24551                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24552             }
24553             
24554             if(this.labelxs > 0){
24555                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24556                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24557             }
24558             
24559         } else if ( this.fieldLabel.length) {
24560 //                Roo.log(" label");
24561                 cfg.cn = [
24562                    
24563                     {
24564                         tag: this.boxLabel ? 'span' : 'label',
24565                         'for': id,
24566                         cls: 'control-label box-input-label',
24567                         //cls : 'input-group-addon',
24568                         html : this.fieldLabel
24569                     },
24570                     
24571                     inputblock
24572                     
24573                 ];
24574                 if (boxLabelCfg) {
24575                     cfg.cn.push(boxLabelCfg);
24576                 }
24577
24578         } else {
24579             
24580 //                Roo.log(" no label && no align");
24581                 cfg.cn = [  inputblock ] ;
24582                 if (boxLabelCfg) {
24583                     cfg.cn.push(boxLabelCfg);
24584                 }
24585
24586                 
24587         }
24588         
24589        
24590         
24591         if(this.inputType != 'radio'){
24592             cfg.cn.push(hidden);
24593         }
24594         
24595         return cfg;
24596         
24597     },
24598     
24599     /**
24600      * return the real input element.
24601      */
24602     inputEl: function ()
24603     {
24604         return this.el.select('input.roo-' + this.inputType,true).first();
24605     },
24606     hiddenEl: function ()
24607     {
24608         return this.el.select('input.roo-hidden-value',true).first();
24609     },
24610     
24611     labelEl: function()
24612     {
24613         return this.el.select('label.control-label',true).first();
24614     },
24615     /* depricated... */
24616     
24617     label: function()
24618     {
24619         return this.labelEl();
24620     },
24621     
24622     boxLabelEl: function()
24623     {
24624         return this.el.select('label.box-label',true).first();
24625     },
24626     
24627     initEvents : function()
24628     {
24629 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24630         
24631         this.inputEl().on('click', this.onClick,  this);
24632         
24633         if (this.boxLabel) { 
24634             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24635         }
24636         
24637         this.startValue = this.getValue();
24638         
24639         if(this.groupId){
24640             Roo.bootstrap.CheckBox.register(this);
24641         }
24642     },
24643     
24644     onClick : function(e)
24645     {   
24646         if(this.fireEvent('click', this, e) !== false){
24647             this.setChecked(!this.checked);
24648         }
24649         
24650     },
24651     
24652     setChecked : function(state,suppressEvent)
24653     {
24654         this.startValue = this.getValue();
24655
24656         if(this.inputType == 'radio'){
24657             
24658             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24659                 e.dom.checked = false;
24660             });
24661             
24662             this.inputEl().dom.checked = true;
24663             
24664             this.inputEl().dom.value = this.inputValue;
24665             
24666             if(suppressEvent !== true){
24667                 this.fireEvent('check', this, true);
24668             }
24669             
24670             this.validate();
24671             
24672             return;
24673         }
24674         
24675         this.checked = state;
24676         
24677         this.inputEl().dom.checked = state;
24678         
24679         
24680         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24681         
24682         if(suppressEvent !== true){
24683             this.fireEvent('check', this, state);
24684         }
24685         
24686         this.validate();
24687     },
24688     
24689     getValue : function()
24690     {
24691         if(this.inputType == 'radio'){
24692             return this.getGroupValue();
24693         }
24694         
24695         return this.hiddenEl().dom.value;
24696         
24697     },
24698     
24699     getGroupValue : function()
24700     {
24701         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24702             return '';
24703         }
24704         
24705         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24706     },
24707     
24708     setValue : function(v,suppressEvent)
24709     {
24710         if(this.inputType == 'radio'){
24711             this.setGroupValue(v, suppressEvent);
24712             return;
24713         }
24714         
24715         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24716         
24717         this.validate();
24718     },
24719     
24720     setGroupValue : function(v, suppressEvent)
24721     {
24722         this.startValue = this.getValue();
24723         
24724         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24725             e.dom.checked = false;
24726             
24727             if(e.dom.value == v){
24728                 e.dom.checked = true;
24729             }
24730         });
24731         
24732         if(suppressEvent !== true){
24733             this.fireEvent('check', this, true);
24734         }
24735
24736         this.validate();
24737         
24738         return;
24739     },
24740     
24741     validate : function()
24742     {
24743         if(this.getVisibilityEl().hasClass('hidden')){
24744             return true;
24745         }
24746         
24747         if(
24748                 this.disabled || 
24749                 (this.inputType == 'radio' && this.validateRadio()) ||
24750                 (this.inputType == 'checkbox' && this.validateCheckbox())
24751         ){
24752             this.markValid();
24753             return true;
24754         }
24755         
24756         this.markInvalid();
24757         return false;
24758     },
24759     
24760     validateRadio : function()
24761     {
24762         if(this.getVisibilityEl().hasClass('hidden')){
24763             return true;
24764         }
24765         
24766         if(this.allowBlank){
24767             return true;
24768         }
24769         
24770         var valid = false;
24771         
24772         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24773             if(!e.dom.checked){
24774                 return;
24775             }
24776             
24777             valid = true;
24778             
24779             return false;
24780         });
24781         
24782         return valid;
24783     },
24784     
24785     validateCheckbox : function()
24786     {
24787         if(!this.groupId){
24788             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24789             //return (this.getValue() == this.inputValue) ? true : false;
24790         }
24791         
24792         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24793         
24794         if(!group){
24795             return false;
24796         }
24797         
24798         var r = false;
24799         
24800         for(var i in group){
24801             if(group[i].el.isVisible(true)){
24802                 r = false;
24803                 break;
24804             }
24805             
24806             r = true;
24807         }
24808         
24809         for(var i in group){
24810             if(r){
24811                 break;
24812             }
24813             
24814             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24815         }
24816         
24817         return r;
24818     },
24819     
24820     /**
24821      * Mark this field as valid
24822      */
24823     markValid : function()
24824     {
24825         var _this = this;
24826         
24827         this.fireEvent('valid', this);
24828         
24829         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24830         
24831         if(this.groupId){
24832             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24833         }
24834         
24835         if(label){
24836             label.markValid();
24837         }
24838
24839         if(this.inputType == 'radio'){
24840             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24841                 var fg = e.findParent('.form-group', false, true);
24842                 if (Roo.bootstrap.version == 3) {
24843                     fg.removeClass([_this.invalidClass, _this.validClass]);
24844                     fg.addClass(_this.validClass);
24845                 } else {
24846                     fg.removeClass(['is-valid', 'is-invalid']);
24847                     fg.addClass('is-valid');
24848                 }
24849             });
24850             
24851             return;
24852         }
24853
24854         if(!this.groupId){
24855             var fg = this.el.findParent('.form-group', false, true);
24856             if (Roo.bootstrap.version == 3) {
24857                 fg.removeClass([this.invalidClass, this.validClass]);
24858                 fg.addClass(this.validClass);
24859             } else {
24860                 fg.removeClass(['is-valid', 'is-invalid']);
24861                 fg.addClass('is-valid');
24862             }
24863             return;
24864         }
24865         
24866         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24867         
24868         if(!group){
24869             return;
24870         }
24871         
24872         for(var i in group){
24873             var fg = group[i].el.findParent('.form-group', false, true);
24874             if (Roo.bootstrap.version == 3) {
24875                 fg.removeClass([this.invalidClass, this.validClass]);
24876                 fg.addClass(this.validClass);
24877             } else {
24878                 fg.removeClass(['is-valid', 'is-invalid']);
24879                 fg.addClass('is-valid');
24880             }
24881         }
24882     },
24883     
24884      /**
24885      * Mark this field as invalid
24886      * @param {String} msg The validation message
24887      */
24888     markInvalid : function(msg)
24889     {
24890         if(this.allowBlank){
24891             return;
24892         }
24893         
24894         var _this = this;
24895         
24896         this.fireEvent('invalid', this, msg);
24897         
24898         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24899         
24900         if(this.groupId){
24901             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24902         }
24903         
24904         if(label){
24905             label.markInvalid();
24906         }
24907             
24908         if(this.inputType == 'radio'){
24909             
24910             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24911                 var fg = e.findParent('.form-group', false, true);
24912                 if (Roo.bootstrap.version == 3) {
24913                     fg.removeClass([_this.invalidClass, _this.validClass]);
24914                     fg.addClass(_this.invalidClass);
24915                 } else {
24916                     fg.removeClass(['is-invalid', 'is-valid']);
24917                     fg.addClass('is-invalid');
24918                 }
24919             });
24920             
24921             return;
24922         }
24923         
24924         if(!this.groupId){
24925             var fg = this.el.findParent('.form-group', false, true);
24926             if (Roo.bootstrap.version == 3) {
24927                 fg.removeClass([_this.invalidClass, _this.validClass]);
24928                 fg.addClass(_this.invalidClass);
24929             } else {
24930                 fg.removeClass(['is-invalid', 'is-valid']);
24931                 fg.addClass('is-invalid');
24932             }
24933             return;
24934         }
24935         
24936         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24937         
24938         if(!group){
24939             return;
24940         }
24941         
24942         for(var i in group){
24943             var fg = group[i].el.findParent('.form-group', false, true);
24944             if (Roo.bootstrap.version == 3) {
24945                 fg.removeClass([_this.invalidClass, _this.validClass]);
24946                 fg.addClass(_this.invalidClass);
24947             } else {
24948                 fg.removeClass(['is-invalid', 'is-valid']);
24949                 fg.addClass('is-invalid');
24950             }
24951         }
24952         
24953     },
24954     
24955     clearInvalid : function()
24956     {
24957         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24958         
24959         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24960         
24961         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24962         
24963         if (label && label.iconEl) {
24964             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24965             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24966         }
24967     },
24968     
24969     disable : function()
24970     {
24971         if(this.inputType != 'radio'){
24972             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24973             return;
24974         }
24975         
24976         var _this = this;
24977         
24978         if(this.rendered){
24979             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24980                 _this.getActionEl().addClass(this.disabledClass);
24981                 e.dom.disabled = true;
24982             });
24983         }
24984         
24985         this.disabled = true;
24986         this.fireEvent("disable", this);
24987         return this;
24988     },
24989
24990     enable : function()
24991     {
24992         if(this.inputType != 'radio'){
24993             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24994             return;
24995         }
24996         
24997         var _this = this;
24998         
24999         if(this.rendered){
25000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25001                 _this.getActionEl().removeClass(this.disabledClass);
25002                 e.dom.disabled = false;
25003             });
25004         }
25005         
25006         this.disabled = false;
25007         this.fireEvent("enable", this);
25008         return this;
25009     },
25010     
25011     setBoxLabel : function(v)
25012     {
25013         this.boxLabel = v;
25014         
25015         if(this.rendered){
25016             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25017         }
25018     }
25019
25020 });
25021
25022 Roo.apply(Roo.bootstrap.CheckBox, {
25023     
25024     groups: {},
25025     
25026      /**
25027     * register a CheckBox Group
25028     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25029     */
25030     register : function(checkbox)
25031     {
25032         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25033             this.groups[checkbox.groupId] = {};
25034         }
25035         
25036         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25037             return;
25038         }
25039         
25040         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25041         
25042     },
25043     /**
25044     * fetch a CheckBox Group based on the group ID
25045     * @param {string} the group ID
25046     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25047     */
25048     get: function(groupId) {
25049         if (typeof(this.groups[groupId]) == 'undefined') {
25050             return false;
25051         }
25052         
25053         return this.groups[groupId] ;
25054     }
25055     
25056     
25057 });
25058 /*
25059  * - LGPL
25060  *
25061  * RadioItem
25062  * 
25063  */
25064
25065 /**
25066  * @class Roo.bootstrap.Radio
25067  * @extends Roo.bootstrap.Component
25068  * Bootstrap Radio class
25069  * @cfg {String} boxLabel - the label associated
25070  * @cfg {String} value - the value of radio
25071  * 
25072  * @constructor
25073  * Create a new Radio
25074  * @param {Object} config The config object
25075  */
25076 Roo.bootstrap.Radio = function(config){
25077     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25078     
25079 };
25080
25081 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25082     
25083     boxLabel : '',
25084     
25085     value : '',
25086     
25087     getAutoCreate : function()
25088     {
25089         var cfg = {
25090             tag : 'div',
25091             cls : 'form-group radio',
25092             cn : [
25093                 {
25094                     tag : 'label',
25095                     cls : 'box-label',
25096                     html : this.boxLabel
25097                 }
25098             ]
25099         };
25100         
25101         return cfg;
25102     },
25103     
25104     initEvents : function() 
25105     {
25106         this.parent().register(this);
25107         
25108         this.el.on('click', this.onClick, this);
25109         
25110     },
25111     
25112     onClick : function(e)
25113     {
25114         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25115             this.setChecked(true);
25116         }
25117     },
25118     
25119     setChecked : function(state, suppressEvent)
25120     {
25121         this.parent().setValue(this.value, suppressEvent);
25122         
25123     },
25124     
25125     setBoxLabel : function(v)
25126     {
25127         this.boxLabel = v;
25128         
25129         if(this.rendered){
25130             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25131         }
25132     }
25133     
25134 });
25135  
25136
25137  /*
25138  * - LGPL
25139  *
25140  * Input
25141  * 
25142  */
25143
25144 /**
25145  * @class Roo.bootstrap.SecurePass
25146  * @extends Roo.bootstrap.Input
25147  * Bootstrap SecurePass class
25148  *
25149  * 
25150  * @constructor
25151  * Create a new SecurePass
25152  * @param {Object} config The config object
25153  */
25154  
25155 Roo.bootstrap.SecurePass = function (config) {
25156     // these go here, so the translation tool can replace them..
25157     this.errors = {
25158         PwdEmpty: "Please type a password, and then retype it to confirm.",
25159         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25160         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25161         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25162         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25163         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25164         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25165         TooWeak: "Your password is Too Weak."
25166     },
25167     this.meterLabel = "Password strength:";
25168     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25169     this.meterClass = [
25170         "roo-password-meter-tooweak", 
25171         "roo-password-meter-weak", 
25172         "roo-password-meter-medium", 
25173         "roo-password-meter-strong", 
25174         "roo-password-meter-grey"
25175     ];
25176     
25177     this.errors = {};
25178     
25179     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25180 }
25181
25182 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25183     /**
25184      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25185      * {
25186      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25187      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25188      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25189      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25190      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25191      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25192      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25193      * })
25194      */
25195     // private
25196     
25197     meterWidth: 300,
25198     errorMsg :'',    
25199     errors: false,
25200     imageRoot: '/',
25201     /**
25202      * @cfg {String/Object} Label for the strength meter (defaults to
25203      * 'Password strength:')
25204      */
25205     // private
25206     meterLabel: '',
25207     /**
25208      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25209      * ['Weak', 'Medium', 'Strong'])
25210      */
25211     // private    
25212     pwdStrengths: false,    
25213     // private
25214     strength: 0,
25215     // private
25216     _lastPwd: null,
25217     // private
25218     kCapitalLetter: 0,
25219     kSmallLetter: 1,
25220     kDigit: 2,
25221     kPunctuation: 3,
25222     
25223     insecure: false,
25224     // private
25225     initEvents: function ()
25226     {
25227         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25228
25229         if (this.el.is('input[type=password]') && Roo.isSafari) {
25230             this.el.on('keydown', this.SafariOnKeyDown, this);
25231         }
25232
25233         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25234     },
25235     // private
25236     onRender: function (ct, position)
25237     {
25238         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25239         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25240         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25241
25242         this.trigger.createChild({
25243                    cn: [
25244                     {
25245                     //id: 'PwdMeter',
25246                     tag: 'div',
25247                     cls: 'roo-password-meter-grey col-xs-12',
25248                     style: {
25249                         //width: 0,
25250                         //width: this.meterWidth + 'px'                                                
25251                         }
25252                     },
25253                     {                            
25254                          cls: 'roo-password-meter-text'                          
25255                     }
25256                 ]            
25257         });
25258
25259          
25260         if (this.hideTrigger) {
25261             this.trigger.setDisplayed(false);
25262         }
25263         this.setSize(this.width || '', this.height || '');
25264     },
25265     // private
25266     onDestroy: function ()
25267     {
25268         if (this.trigger) {
25269             this.trigger.removeAllListeners();
25270             this.trigger.remove();
25271         }
25272         if (this.wrap) {
25273             this.wrap.remove();
25274         }
25275         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25276     },
25277     // private
25278     checkStrength: function ()
25279     {
25280         var pwd = this.inputEl().getValue();
25281         if (pwd == this._lastPwd) {
25282             return;
25283         }
25284
25285         var strength;
25286         if (this.ClientSideStrongPassword(pwd)) {
25287             strength = 3;
25288         } else if (this.ClientSideMediumPassword(pwd)) {
25289             strength = 2;
25290         } else if (this.ClientSideWeakPassword(pwd)) {
25291             strength = 1;
25292         } else {
25293             strength = 0;
25294         }
25295         
25296         Roo.log('strength1: ' + strength);
25297         
25298         //var pm = this.trigger.child('div/div/div').dom;
25299         var pm = this.trigger.child('div/div');
25300         pm.removeClass(this.meterClass);
25301         pm.addClass(this.meterClass[strength]);
25302                 
25303         
25304         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25305                 
25306         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25307         
25308         this._lastPwd = pwd;
25309     },
25310     reset: function ()
25311     {
25312         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25313         
25314         this._lastPwd = '';
25315         
25316         var pm = this.trigger.child('div/div');
25317         pm.removeClass(this.meterClass);
25318         pm.addClass('roo-password-meter-grey');        
25319         
25320         
25321         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25322         
25323         pt.innerHTML = '';
25324         this.inputEl().dom.type='password';
25325     },
25326     // private
25327     validateValue: function (value)
25328     {
25329         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25330             return false;
25331         }
25332         if (value.length == 0) {
25333             if (this.allowBlank) {
25334                 this.clearInvalid();
25335                 return true;
25336             }
25337
25338             this.markInvalid(this.errors.PwdEmpty);
25339             this.errorMsg = this.errors.PwdEmpty;
25340             return false;
25341         }
25342         
25343         if(this.insecure){
25344             return true;
25345         }
25346         
25347         if (!value.match(/[\x21-\x7e]+/)) {
25348             this.markInvalid(this.errors.PwdBadChar);
25349             this.errorMsg = this.errors.PwdBadChar;
25350             return false;
25351         }
25352         if (value.length < 6) {
25353             this.markInvalid(this.errors.PwdShort);
25354             this.errorMsg = this.errors.PwdShort;
25355             return false;
25356         }
25357         if (value.length > 16) {
25358             this.markInvalid(this.errors.PwdLong);
25359             this.errorMsg = this.errors.PwdLong;
25360             return false;
25361         }
25362         var strength;
25363         if (this.ClientSideStrongPassword(value)) {
25364             strength = 3;
25365         } else if (this.ClientSideMediumPassword(value)) {
25366             strength = 2;
25367         } else if (this.ClientSideWeakPassword(value)) {
25368             strength = 1;
25369         } else {
25370             strength = 0;
25371         }
25372
25373         
25374         if (strength < 2) {
25375             //this.markInvalid(this.errors.TooWeak);
25376             this.errorMsg = this.errors.TooWeak;
25377             //return false;
25378         }
25379         
25380         
25381         console.log('strength2: ' + strength);
25382         
25383         //var pm = this.trigger.child('div/div/div').dom;
25384         
25385         var pm = this.trigger.child('div/div');
25386         pm.removeClass(this.meterClass);
25387         pm.addClass(this.meterClass[strength]);
25388                 
25389         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25390                 
25391         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25392         
25393         this.errorMsg = ''; 
25394         return true;
25395     },
25396     // private
25397     CharacterSetChecks: function (type)
25398     {
25399         this.type = type;
25400         this.fResult = false;
25401     },
25402     // private
25403     isctype: function (character, type)
25404     {
25405         switch (type) {  
25406             case this.kCapitalLetter:
25407                 if (character >= 'A' && character <= 'Z') {
25408                     return true;
25409                 }
25410                 break;
25411             
25412             case this.kSmallLetter:
25413                 if (character >= 'a' && character <= 'z') {
25414                     return true;
25415                 }
25416                 break;
25417             
25418             case this.kDigit:
25419                 if (character >= '0' && character <= '9') {
25420                     return true;
25421                 }
25422                 break;
25423             
25424             case this.kPunctuation:
25425                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25426                     return true;
25427                 }
25428                 break;
25429             
25430             default:
25431                 return false;
25432         }
25433
25434     },
25435     // private
25436     IsLongEnough: function (pwd, size)
25437     {
25438         return !(pwd == null || isNaN(size) || pwd.length < size);
25439     },
25440     // private
25441     SpansEnoughCharacterSets: function (word, nb)
25442     {
25443         if (!this.IsLongEnough(word, nb))
25444         {
25445             return false;
25446         }
25447
25448         var characterSetChecks = new Array(
25449             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25450             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25451         );
25452         
25453         for (var index = 0; index < word.length; ++index) {
25454             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25455                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25456                     characterSetChecks[nCharSet].fResult = true;
25457                     break;
25458                 }
25459             }
25460         }
25461
25462         var nCharSets = 0;
25463         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25464             if (characterSetChecks[nCharSet].fResult) {
25465                 ++nCharSets;
25466             }
25467         }
25468
25469         if (nCharSets < nb) {
25470             return false;
25471         }
25472         return true;
25473     },
25474     // private
25475     ClientSideStrongPassword: function (pwd)
25476     {
25477         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25478     },
25479     // private
25480     ClientSideMediumPassword: function (pwd)
25481     {
25482         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25483     },
25484     // private
25485     ClientSideWeakPassword: function (pwd)
25486     {
25487         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25488     }
25489           
25490 })//<script type="text/javascript">
25491
25492 /*
25493  * Based  Ext JS Library 1.1.1
25494  * Copyright(c) 2006-2007, Ext JS, LLC.
25495  * LGPL
25496  *
25497  */
25498  
25499 /**
25500  * @class Roo.HtmlEditorCore
25501  * @extends Roo.Component
25502  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25503  *
25504  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25505  */
25506
25507 Roo.HtmlEditorCore = function(config){
25508     
25509     
25510     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25511     
25512     
25513     this.addEvents({
25514         /**
25515          * @event initialize
25516          * Fires when the editor is fully initialized (including the iframe)
25517          * @param {Roo.HtmlEditorCore} this
25518          */
25519         initialize: true,
25520         /**
25521          * @event activate
25522          * Fires when the editor is first receives the focus. Any insertion must wait
25523          * until after this event.
25524          * @param {Roo.HtmlEditorCore} this
25525          */
25526         activate: true,
25527          /**
25528          * @event beforesync
25529          * Fires before the textarea is updated with content from the editor iframe. Return false
25530          * to cancel the sync.
25531          * @param {Roo.HtmlEditorCore} this
25532          * @param {String} html
25533          */
25534         beforesync: true,
25535          /**
25536          * @event beforepush
25537          * Fires before the iframe editor is updated with content from the textarea. Return false
25538          * to cancel the push.
25539          * @param {Roo.HtmlEditorCore} this
25540          * @param {String} html
25541          */
25542         beforepush: true,
25543          /**
25544          * @event sync
25545          * Fires when the textarea is updated with content from the editor iframe.
25546          * @param {Roo.HtmlEditorCore} this
25547          * @param {String} html
25548          */
25549         sync: true,
25550          /**
25551          * @event push
25552          * Fires when the iframe editor is updated with content from the textarea.
25553          * @param {Roo.HtmlEditorCore} this
25554          * @param {String} html
25555          */
25556         push: true,
25557         
25558         /**
25559          * @event editorevent
25560          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25561          * @param {Roo.HtmlEditorCore} this
25562          */
25563         editorevent: true
25564         
25565     });
25566     
25567     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25568     
25569     // defaults : white / black...
25570     this.applyBlacklists();
25571     
25572     
25573     
25574 };
25575
25576
25577 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25578
25579
25580      /**
25581      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25582      */
25583     
25584     owner : false,
25585     
25586      /**
25587      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25588      *                        Roo.resizable.
25589      */
25590     resizable : false,
25591      /**
25592      * @cfg {Number} height (in pixels)
25593      */   
25594     height: 300,
25595    /**
25596      * @cfg {Number} width (in pixels)
25597      */   
25598     width: 500,
25599     
25600     /**
25601      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25602      * 
25603      */
25604     stylesheets: false,
25605     
25606     // id of frame..
25607     frameId: false,
25608     
25609     // private properties
25610     validationEvent : false,
25611     deferHeight: true,
25612     initialized : false,
25613     activated : false,
25614     sourceEditMode : false,
25615     onFocus : Roo.emptyFn,
25616     iframePad:3,
25617     hideMode:'offsets',
25618     
25619     clearUp: true,
25620     
25621     // blacklist + whitelisted elements..
25622     black: false,
25623     white: false,
25624      
25625     bodyCls : '',
25626
25627     /**
25628      * Protected method that will not generally be called directly. It
25629      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25630      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25631      */
25632     getDocMarkup : function(){
25633         // body styles..
25634         var st = '';
25635         
25636         // inherit styels from page...?? 
25637         if (this.stylesheets === false) {
25638             
25639             Roo.get(document.head).select('style').each(function(node) {
25640                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25641             });
25642             
25643             Roo.get(document.head).select('link').each(function(node) { 
25644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25645             });
25646             
25647         } else if (!this.stylesheets.length) {
25648                 // simple..
25649                 st = '<style type="text/css">' +
25650                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25651                    '</style>';
25652         } else {
25653             for (var i in this.stylesheets) { 
25654                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25655             }
25656             
25657         }
25658         
25659         st +=  '<style type="text/css">' +
25660             'IMG { cursor: pointer } ' +
25661         '</style>';
25662
25663         var cls = 'roo-htmleditor-body';
25664         
25665         if(this.bodyCls.length){
25666             cls += ' ' + this.bodyCls;
25667         }
25668         
25669         return '<html><head>' + st  +
25670             //<style type="text/css">' +
25671             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25672             //'</style>' +
25673             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25674     },
25675
25676     // private
25677     onRender : function(ct, position)
25678     {
25679         var _t = this;
25680         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25681         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25682         
25683         
25684         this.el.dom.style.border = '0 none';
25685         this.el.dom.setAttribute('tabIndex', -1);
25686         this.el.addClass('x-hidden hide');
25687         
25688         
25689         
25690         if(Roo.isIE){ // fix IE 1px bogus margin
25691             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25692         }
25693        
25694         
25695         this.frameId = Roo.id();
25696         
25697          
25698         
25699         var iframe = this.owner.wrap.createChild({
25700             tag: 'iframe',
25701             cls: 'form-control', // bootstrap..
25702             id: this.frameId,
25703             name: this.frameId,
25704             frameBorder : 'no',
25705             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25706         }, this.el
25707         );
25708         
25709         
25710         this.iframe = iframe.dom;
25711
25712          this.assignDocWin();
25713         
25714         this.doc.designMode = 'on';
25715        
25716         this.doc.open();
25717         this.doc.write(this.getDocMarkup());
25718         this.doc.close();
25719
25720         
25721         var task = { // must defer to wait for browser to be ready
25722             run : function(){
25723                 //console.log("run task?" + this.doc.readyState);
25724                 this.assignDocWin();
25725                 if(this.doc.body || this.doc.readyState == 'complete'){
25726                     try {
25727                         this.doc.designMode="on";
25728                     } catch (e) {
25729                         return;
25730                     }
25731                     Roo.TaskMgr.stop(task);
25732                     this.initEditor.defer(10, this);
25733                 }
25734             },
25735             interval : 10,
25736             duration: 10000,
25737             scope: this
25738         };
25739         Roo.TaskMgr.start(task);
25740
25741     },
25742
25743     // private
25744     onResize : function(w, h)
25745     {
25746          Roo.log('resize: ' +w + ',' + h );
25747         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25748         if(!this.iframe){
25749             return;
25750         }
25751         if(typeof w == 'number'){
25752             
25753             this.iframe.style.width = w + 'px';
25754         }
25755         if(typeof h == 'number'){
25756             
25757             this.iframe.style.height = h + 'px';
25758             if(this.doc){
25759                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25760             }
25761         }
25762         
25763     },
25764
25765     /**
25766      * Toggles the editor between standard and source edit mode.
25767      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25768      */
25769     toggleSourceEdit : function(sourceEditMode){
25770         
25771         this.sourceEditMode = sourceEditMode === true;
25772         
25773         if(this.sourceEditMode){
25774  
25775             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25776             
25777         }else{
25778             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25779             //this.iframe.className = '';
25780             this.deferFocus();
25781         }
25782         //this.setSize(this.owner.wrap.getSize());
25783         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25784     },
25785
25786     
25787   
25788
25789     /**
25790      * Protected method that will not generally be called directly. If you need/want
25791      * custom HTML cleanup, this is the method you should override.
25792      * @param {String} html The HTML to be cleaned
25793      * return {String} The cleaned HTML
25794      */
25795     cleanHtml : function(html){
25796         html = String(html);
25797         if(html.length > 5){
25798             if(Roo.isSafari){ // strip safari nonsense
25799                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25800             }
25801         }
25802         if(html == '&nbsp;'){
25803             html = '';
25804         }
25805         return html;
25806     },
25807
25808     /**
25809      * HTML Editor -> Textarea
25810      * Protected method that will not generally be called directly. Syncs the contents
25811      * of the editor iframe with the textarea.
25812      */
25813     syncValue : function(){
25814         if(this.initialized){
25815             var bd = (this.doc.body || this.doc.documentElement);
25816             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25817             var html = bd.innerHTML;
25818             if(Roo.isSafari){
25819                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25820                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25821                 if(m && m[1]){
25822                     html = '<div style="'+m[0]+'">' + html + '</div>';
25823                 }
25824             }
25825             html = this.cleanHtml(html);
25826             // fix up the special chars.. normaly like back quotes in word...
25827             // however we do not want to do this with chinese..
25828             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25829                 
25830                 var cc = match.charCodeAt();
25831
25832                 // Get the character value, handling surrogate pairs
25833                 if (match.length == 2) {
25834                     // It's a surrogate pair, calculate the Unicode code point
25835                     var high = match.charCodeAt(0) - 0xD800;
25836                     var low  = match.charCodeAt(1) - 0xDC00;
25837                     cc = (high * 0x400) + low + 0x10000;
25838                 }  else if (
25839                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25840                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25841                     (cc >= 0xf900 && cc < 0xfb00 )
25842                 ) {
25843                         return match;
25844                 }  
25845          
25846                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25847                 return "&#" + cc + ";";
25848                 
25849                 
25850             });
25851             
25852             
25853              
25854             if(this.owner.fireEvent('beforesync', this, html) !== false){
25855                 this.el.dom.value = html;
25856                 this.owner.fireEvent('sync', this, html);
25857             }
25858         }
25859     },
25860
25861     /**
25862      * Protected method that will not generally be called directly. Pushes the value of the textarea
25863      * into the iframe editor.
25864      */
25865     pushValue : function(){
25866         if(this.initialized){
25867             var v = this.el.dom.value.trim();
25868             
25869 //            if(v.length < 1){
25870 //                v = '&#160;';
25871 //            }
25872             
25873             if(this.owner.fireEvent('beforepush', this, v) !== false){
25874                 var d = (this.doc.body || this.doc.documentElement);
25875                 d.innerHTML = v;
25876                 this.cleanUpPaste();
25877                 this.el.dom.value = d.innerHTML;
25878                 this.owner.fireEvent('push', this, v);
25879             }
25880         }
25881     },
25882
25883     // private
25884     deferFocus : function(){
25885         this.focus.defer(10, this);
25886     },
25887
25888     // doc'ed in Field
25889     focus : function(){
25890         if(this.win && !this.sourceEditMode){
25891             this.win.focus();
25892         }else{
25893             this.el.focus();
25894         }
25895     },
25896     
25897     assignDocWin: function()
25898     {
25899         var iframe = this.iframe;
25900         
25901          if(Roo.isIE){
25902             this.doc = iframe.contentWindow.document;
25903             this.win = iframe.contentWindow;
25904         } else {
25905 //            if (!Roo.get(this.frameId)) {
25906 //                return;
25907 //            }
25908 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25909 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25910             
25911             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25912                 return;
25913             }
25914             
25915             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25916             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25917         }
25918     },
25919     
25920     // private
25921     initEditor : function(){
25922         //console.log("INIT EDITOR");
25923         this.assignDocWin();
25924         
25925         
25926         
25927         this.doc.designMode="on";
25928         this.doc.open();
25929         this.doc.write(this.getDocMarkup());
25930         this.doc.close();
25931         
25932         var dbody = (this.doc.body || this.doc.documentElement);
25933         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25934         // this copies styles from the containing element into thsi one..
25935         // not sure why we need all of this..
25936         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25937         
25938         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25939         //ss['background-attachment'] = 'fixed'; // w3c
25940         dbody.bgProperties = 'fixed'; // ie
25941         //Roo.DomHelper.applyStyles(dbody, ss);
25942         Roo.EventManager.on(this.doc, {
25943             //'mousedown': this.onEditorEvent,
25944             'mouseup': this.onEditorEvent,
25945             'dblclick': this.onEditorEvent,
25946             'click': this.onEditorEvent,
25947             'keyup': this.onEditorEvent,
25948             buffer:100,
25949             scope: this
25950         });
25951         if(Roo.isGecko){
25952             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25953         }
25954         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25955             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25956         }
25957         this.initialized = true;
25958
25959         this.owner.fireEvent('initialize', this);
25960         this.pushValue();
25961     },
25962
25963     // private
25964     onDestroy : function(){
25965         
25966         
25967         
25968         if(this.rendered){
25969             
25970             //for (var i =0; i < this.toolbars.length;i++) {
25971             //    // fixme - ask toolbars for heights?
25972             //    this.toolbars[i].onDestroy();
25973            // }
25974             
25975             //this.wrap.dom.innerHTML = '';
25976             //this.wrap.remove();
25977         }
25978     },
25979
25980     // private
25981     onFirstFocus : function(){
25982         
25983         this.assignDocWin();
25984         
25985         
25986         this.activated = true;
25987          
25988     
25989         if(Roo.isGecko){ // prevent silly gecko errors
25990             this.win.focus();
25991             var s = this.win.getSelection();
25992             if(!s.focusNode || s.focusNode.nodeType != 3){
25993                 var r = s.getRangeAt(0);
25994                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25995                 r.collapse(true);
25996                 this.deferFocus();
25997             }
25998             try{
25999                 this.execCmd('useCSS', true);
26000                 this.execCmd('styleWithCSS', false);
26001             }catch(e){}
26002         }
26003         this.owner.fireEvent('activate', this);
26004     },
26005
26006     // private
26007     adjustFont: function(btn){
26008         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26009         //if(Roo.isSafari){ // safari
26010         //    adjust *= 2;
26011        // }
26012         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26013         if(Roo.isSafari){ // safari
26014             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26015             v =  (v < 10) ? 10 : v;
26016             v =  (v > 48) ? 48 : v;
26017             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26018             
26019         }
26020         
26021         
26022         v = Math.max(1, v+adjust);
26023         
26024         this.execCmd('FontSize', v  );
26025     },
26026
26027     onEditorEvent : function(e)
26028     {
26029         this.owner.fireEvent('editorevent', this, e);
26030       //  this.updateToolbar();
26031         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26032     },
26033
26034     insertTag : function(tg)
26035     {
26036         // could be a bit smarter... -> wrap the current selected tRoo..
26037         if (tg.toLowerCase() == 'span' ||
26038             tg.toLowerCase() == 'code' ||
26039             tg.toLowerCase() == 'sup' ||
26040             tg.toLowerCase() == 'sub' 
26041             ) {
26042             
26043             range = this.createRange(this.getSelection());
26044             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26045             wrappingNode.appendChild(range.extractContents());
26046             range.insertNode(wrappingNode);
26047
26048             return;
26049             
26050             
26051             
26052         }
26053         this.execCmd("formatblock",   tg);
26054         
26055     },
26056     
26057     insertText : function(txt)
26058     {
26059         
26060         
26061         var range = this.createRange();
26062         range.deleteContents();
26063                //alert(Sender.getAttribute('label'));
26064                
26065         range.insertNode(this.doc.createTextNode(txt));
26066     } ,
26067     
26068      
26069
26070     /**
26071      * Executes a Midas editor command on the editor document and performs necessary focus and
26072      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26073      * @param {String} cmd The Midas command
26074      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26075      */
26076     relayCmd : function(cmd, value){
26077         this.win.focus();
26078         this.execCmd(cmd, value);
26079         this.owner.fireEvent('editorevent', this);
26080         //this.updateToolbar();
26081         this.owner.deferFocus();
26082     },
26083
26084     /**
26085      * Executes a Midas editor command directly on the editor document.
26086      * For visual commands, you should use {@link #relayCmd} instead.
26087      * <b>This should only be called after the editor is initialized.</b>
26088      * @param {String} cmd The Midas command
26089      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26090      */
26091     execCmd : function(cmd, value){
26092         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26093         this.syncValue();
26094     },
26095  
26096  
26097    
26098     /**
26099      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26100      * to insert tRoo.
26101      * @param {String} text | dom node.. 
26102      */
26103     insertAtCursor : function(text)
26104     {
26105         
26106         if(!this.activated){
26107             return;
26108         }
26109         /*
26110         if(Roo.isIE){
26111             this.win.focus();
26112             var r = this.doc.selection.createRange();
26113             if(r){
26114                 r.collapse(true);
26115                 r.pasteHTML(text);
26116                 this.syncValue();
26117                 this.deferFocus();
26118             
26119             }
26120             return;
26121         }
26122         */
26123         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26124             this.win.focus();
26125             
26126             
26127             // from jquery ui (MIT licenced)
26128             var range, node;
26129             var win = this.win;
26130             
26131             if (win.getSelection && win.getSelection().getRangeAt) {
26132                 range = win.getSelection().getRangeAt(0);
26133                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26134                 range.insertNode(node);
26135             } else if (win.document.selection && win.document.selection.createRange) {
26136                 // no firefox support
26137                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26138                 win.document.selection.createRange().pasteHTML(txt);
26139             } else {
26140                 // no firefox support
26141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26142                 this.execCmd('InsertHTML', txt);
26143             } 
26144             
26145             this.syncValue();
26146             
26147             this.deferFocus();
26148         }
26149     },
26150  // private
26151     mozKeyPress : function(e){
26152         if(e.ctrlKey){
26153             var c = e.getCharCode(), cmd;
26154           
26155             if(c > 0){
26156                 c = String.fromCharCode(c).toLowerCase();
26157                 switch(c){
26158                     case 'b':
26159                         cmd = 'bold';
26160                         break;
26161                     case 'i':
26162                         cmd = 'italic';
26163                         break;
26164                     
26165                     case 'u':
26166                         cmd = 'underline';
26167                         break;
26168                     
26169                     case 'v':
26170                         this.cleanUpPaste.defer(100, this);
26171                         return;
26172                         
26173                 }
26174                 if(cmd){
26175                     this.win.focus();
26176                     this.execCmd(cmd);
26177                     this.deferFocus();
26178                     e.preventDefault();
26179                 }
26180                 
26181             }
26182         }
26183     },
26184
26185     // private
26186     fixKeys : function(){ // load time branching for fastest keydown performance
26187         if(Roo.isIE){
26188             return function(e){
26189                 var k = e.getKey(), r;
26190                 if(k == e.TAB){
26191                     e.stopEvent();
26192                     r = this.doc.selection.createRange();
26193                     if(r){
26194                         r.collapse(true);
26195                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26196                         this.deferFocus();
26197                     }
26198                     return;
26199                 }
26200                 
26201                 if(k == e.ENTER){
26202                     r = this.doc.selection.createRange();
26203                     if(r){
26204                         var target = r.parentElement();
26205                         if(!target || target.tagName.toLowerCase() != 'li'){
26206                             e.stopEvent();
26207                             r.pasteHTML('<br />');
26208                             r.collapse(false);
26209                             r.select();
26210                         }
26211                     }
26212                 }
26213                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26214                     this.cleanUpPaste.defer(100, this);
26215                     return;
26216                 }
26217                 
26218                 
26219             };
26220         }else if(Roo.isOpera){
26221             return function(e){
26222                 var k = e.getKey();
26223                 if(k == e.TAB){
26224                     e.stopEvent();
26225                     this.win.focus();
26226                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26227                     this.deferFocus();
26228                 }
26229                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26230                     this.cleanUpPaste.defer(100, this);
26231                     return;
26232                 }
26233                 
26234             };
26235         }else if(Roo.isSafari){
26236             return function(e){
26237                 var k = e.getKey();
26238                 
26239                 if(k == e.TAB){
26240                     e.stopEvent();
26241                     this.execCmd('InsertText','\t');
26242                     this.deferFocus();
26243                     return;
26244                 }
26245                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26246                     this.cleanUpPaste.defer(100, this);
26247                     return;
26248                 }
26249                 
26250              };
26251         }
26252     }(),
26253     
26254     getAllAncestors: function()
26255     {
26256         var p = this.getSelectedNode();
26257         var a = [];
26258         if (!p) {
26259             a.push(p); // push blank onto stack..
26260             p = this.getParentElement();
26261         }
26262         
26263         
26264         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26265             a.push(p);
26266             p = p.parentNode;
26267         }
26268         a.push(this.doc.body);
26269         return a;
26270     },
26271     lastSel : false,
26272     lastSelNode : false,
26273     
26274     
26275     getSelection : function() 
26276     {
26277         this.assignDocWin();
26278         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26279     },
26280     
26281     getSelectedNode: function() 
26282     {
26283         // this may only work on Gecko!!!
26284         
26285         // should we cache this!!!!
26286         
26287         
26288         
26289          
26290         var range = this.createRange(this.getSelection()).cloneRange();
26291         
26292         if (Roo.isIE) {
26293             var parent = range.parentElement();
26294             while (true) {
26295                 var testRange = range.duplicate();
26296                 testRange.moveToElementText(parent);
26297                 if (testRange.inRange(range)) {
26298                     break;
26299                 }
26300                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26301                     break;
26302                 }
26303                 parent = parent.parentElement;
26304             }
26305             return parent;
26306         }
26307         
26308         // is ancestor a text element.
26309         var ac =  range.commonAncestorContainer;
26310         if (ac.nodeType == 3) {
26311             ac = ac.parentNode;
26312         }
26313         
26314         var ar = ac.childNodes;
26315          
26316         var nodes = [];
26317         var other_nodes = [];
26318         var has_other_nodes = false;
26319         for (var i=0;i<ar.length;i++) {
26320             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26321                 continue;
26322             }
26323             // fullly contained node.
26324             
26325             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26326                 nodes.push(ar[i]);
26327                 continue;
26328             }
26329             
26330             // probably selected..
26331             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26332                 other_nodes.push(ar[i]);
26333                 continue;
26334             }
26335             // outer..
26336             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26337                 continue;
26338             }
26339             
26340             
26341             has_other_nodes = true;
26342         }
26343         if (!nodes.length && other_nodes.length) {
26344             nodes= other_nodes;
26345         }
26346         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26347             return false;
26348         }
26349         
26350         return nodes[0];
26351     },
26352     createRange: function(sel)
26353     {
26354         // this has strange effects when using with 
26355         // top toolbar - not sure if it's a great idea.
26356         //this.editor.contentWindow.focus();
26357         if (typeof sel != "undefined") {
26358             try {
26359                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26360             } catch(e) {
26361                 return this.doc.createRange();
26362             }
26363         } else {
26364             return this.doc.createRange();
26365         }
26366     },
26367     getParentElement: function()
26368     {
26369         
26370         this.assignDocWin();
26371         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26372         
26373         var range = this.createRange(sel);
26374          
26375         try {
26376             var p = range.commonAncestorContainer;
26377             while (p.nodeType == 3) { // text node
26378                 p = p.parentNode;
26379             }
26380             return p;
26381         } catch (e) {
26382             return null;
26383         }
26384     
26385     },
26386     /***
26387      *
26388      * Range intersection.. the hard stuff...
26389      *  '-1' = before
26390      *  '0' = hits..
26391      *  '1' = after.
26392      *         [ -- selected range --- ]
26393      *   [fail]                        [fail]
26394      *
26395      *    basically..
26396      *      if end is before start or  hits it. fail.
26397      *      if start is after end or hits it fail.
26398      *
26399      *   if either hits (but other is outside. - then it's not 
26400      *   
26401      *    
26402      **/
26403     
26404     
26405     // @see http://www.thismuchiknow.co.uk/?p=64.
26406     rangeIntersectsNode : function(range, node)
26407     {
26408         var nodeRange = node.ownerDocument.createRange();
26409         try {
26410             nodeRange.selectNode(node);
26411         } catch (e) {
26412             nodeRange.selectNodeContents(node);
26413         }
26414     
26415         var rangeStartRange = range.cloneRange();
26416         rangeStartRange.collapse(true);
26417     
26418         var rangeEndRange = range.cloneRange();
26419         rangeEndRange.collapse(false);
26420     
26421         var nodeStartRange = nodeRange.cloneRange();
26422         nodeStartRange.collapse(true);
26423     
26424         var nodeEndRange = nodeRange.cloneRange();
26425         nodeEndRange.collapse(false);
26426     
26427         return rangeStartRange.compareBoundaryPoints(
26428                  Range.START_TO_START, nodeEndRange) == -1 &&
26429                rangeEndRange.compareBoundaryPoints(
26430                  Range.START_TO_START, nodeStartRange) == 1;
26431         
26432          
26433     },
26434     rangeCompareNode : function(range, node)
26435     {
26436         var nodeRange = node.ownerDocument.createRange();
26437         try {
26438             nodeRange.selectNode(node);
26439         } catch (e) {
26440             nodeRange.selectNodeContents(node);
26441         }
26442         
26443         
26444         range.collapse(true);
26445     
26446         nodeRange.collapse(true);
26447      
26448         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26449         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26450          
26451         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26452         
26453         var nodeIsBefore   =  ss == 1;
26454         var nodeIsAfter    = ee == -1;
26455         
26456         if (nodeIsBefore && nodeIsAfter) {
26457             return 0; // outer
26458         }
26459         if (!nodeIsBefore && nodeIsAfter) {
26460             return 1; //right trailed.
26461         }
26462         
26463         if (nodeIsBefore && !nodeIsAfter) {
26464             return 2;  // left trailed.
26465         }
26466         // fully contined.
26467         return 3;
26468     },
26469
26470     // private? - in a new class?
26471     cleanUpPaste :  function()
26472     {
26473         // cleans up the whole document..
26474         Roo.log('cleanuppaste');
26475         
26476         this.cleanUpChildren(this.doc.body);
26477         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26478         if (clean != this.doc.body.innerHTML) {
26479             this.doc.body.innerHTML = clean;
26480         }
26481         
26482     },
26483     
26484     cleanWordChars : function(input) {// change the chars to hex code
26485         var he = Roo.HtmlEditorCore;
26486         
26487         var output = input;
26488         Roo.each(he.swapCodes, function(sw) { 
26489             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26490             
26491             output = output.replace(swapper, sw[1]);
26492         });
26493         
26494         return output;
26495     },
26496     
26497     
26498     cleanUpChildren : function (n)
26499     {
26500         if (!n.childNodes.length) {
26501             return;
26502         }
26503         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26504            this.cleanUpChild(n.childNodes[i]);
26505         }
26506     },
26507     
26508     
26509         
26510     
26511     cleanUpChild : function (node)
26512     {
26513         var ed = this;
26514         //console.log(node);
26515         if (node.nodeName == "#text") {
26516             // clean up silly Windows -- stuff?
26517             return; 
26518         }
26519         if (node.nodeName == "#comment") {
26520             node.parentNode.removeChild(node);
26521             // clean up silly Windows -- stuff?
26522             return; 
26523         }
26524         var lcname = node.tagName.toLowerCase();
26525         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26526         // whitelist of tags..
26527         
26528         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26529             // remove node.
26530             node.parentNode.removeChild(node);
26531             return;
26532             
26533         }
26534         
26535         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26536         
26537         // spans with no attributes - just remove them..
26538         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26539             remove_keep_children = true;
26540         }
26541         
26542         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26543         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26544         
26545         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26546         //    remove_keep_children = true;
26547         //}
26548         
26549         if (remove_keep_children) {
26550             this.cleanUpChildren(node);
26551             // inserts everything just before this node...
26552             while (node.childNodes.length) {
26553                 var cn = node.childNodes[0];
26554                 node.removeChild(cn);
26555                 node.parentNode.insertBefore(cn, node);
26556             }
26557             node.parentNode.removeChild(node);
26558             return;
26559         }
26560         
26561         if (!node.attributes || !node.attributes.length) {
26562             
26563           
26564             
26565             
26566             this.cleanUpChildren(node);
26567             return;
26568         }
26569         
26570         function cleanAttr(n,v)
26571         {
26572             
26573             if (v.match(/^\./) || v.match(/^\//)) {
26574                 return;
26575             }
26576             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26577                 return;
26578             }
26579             if (v.match(/^#/)) {
26580                 return;
26581             }
26582             if (v.match(/^\{/)) { // allow template editing.
26583                 return;
26584             }
26585 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26586             node.removeAttribute(n);
26587             
26588         }
26589         
26590         var cwhite = this.cwhite;
26591         var cblack = this.cblack;
26592             
26593         function cleanStyle(n,v)
26594         {
26595             if (v.match(/expression/)) { //XSS?? should we even bother..
26596                 node.removeAttribute(n);
26597                 return;
26598             }
26599             
26600             var parts = v.split(/;/);
26601             var clean = [];
26602             
26603             Roo.each(parts, function(p) {
26604                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26605                 if (!p.length) {
26606                     return true;
26607                 }
26608                 var l = p.split(':').shift().replace(/\s+/g,'');
26609                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26610                 
26611                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26612 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26613                     //node.removeAttribute(n);
26614                     return true;
26615                 }
26616                 //Roo.log()
26617                 // only allow 'c whitelisted system attributes'
26618                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26619 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26620                     //node.removeAttribute(n);
26621                     return true;
26622                 }
26623                 
26624                 
26625                  
26626                 
26627                 clean.push(p);
26628                 return true;
26629             });
26630             if (clean.length) { 
26631                 node.setAttribute(n, clean.join(';'));
26632             } else {
26633                 node.removeAttribute(n);
26634             }
26635             
26636         }
26637         
26638         
26639         for (var i = node.attributes.length-1; i > -1 ; i--) {
26640             var a = node.attributes[i];
26641             //console.log(a);
26642             
26643             if (a.name.toLowerCase().substr(0,2)=='on')  {
26644                 node.removeAttribute(a.name);
26645                 continue;
26646             }
26647             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26648                 node.removeAttribute(a.name);
26649                 continue;
26650             }
26651             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26652                 cleanAttr(a.name,a.value); // fixme..
26653                 continue;
26654             }
26655             if (a.name == 'style') {
26656                 cleanStyle(a.name,a.value);
26657                 continue;
26658             }
26659             /// clean up MS crap..
26660             // tecnically this should be a list of valid class'es..
26661             
26662             
26663             if (a.name == 'class') {
26664                 if (a.value.match(/^Mso/)) {
26665                     node.removeAttribute('class');
26666                 }
26667                 
26668                 if (a.value.match(/^body$/)) {
26669                     node.removeAttribute('class');
26670                 }
26671                 continue;
26672             }
26673             
26674             // style cleanup!?
26675             // class cleanup?
26676             
26677         }
26678         
26679         
26680         this.cleanUpChildren(node);
26681         
26682         
26683     },
26684     
26685     /**
26686      * Clean up MS wordisms...
26687      */
26688     cleanWord : function(node)
26689     {
26690         if (!node) {
26691             this.cleanWord(this.doc.body);
26692             return;
26693         }
26694         
26695         if(
26696                 node.nodeName == 'SPAN' &&
26697                 !node.hasAttributes() &&
26698                 node.childNodes.length == 1 &&
26699                 node.firstChild.nodeName == "#text"  
26700         ) {
26701             var textNode = node.firstChild;
26702             node.removeChild(textNode);
26703             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26704                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26705             }
26706             node.parentNode.insertBefore(textNode, node);
26707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26709             }
26710             node.parentNode.removeChild(node);
26711         }
26712         
26713         if (node.nodeName == "#text") {
26714             // clean up silly Windows -- stuff?
26715             return; 
26716         }
26717         if (node.nodeName == "#comment") {
26718             node.parentNode.removeChild(node);
26719             // clean up silly Windows -- stuff?
26720             return; 
26721         }
26722         
26723         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26724             node.parentNode.removeChild(node);
26725             return;
26726         }
26727         //Roo.log(node.tagName);
26728         // remove - but keep children..
26729         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26730             //Roo.log('-- removed');
26731             while (node.childNodes.length) {
26732                 var cn = node.childNodes[0];
26733                 node.removeChild(cn);
26734                 node.parentNode.insertBefore(cn, node);
26735                 // move node to parent - and clean it..
26736                 this.cleanWord(cn);
26737             }
26738             node.parentNode.removeChild(node);
26739             /// no need to iterate chidlren = it's got none..
26740             //this.iterateChildren(node, this.cleanWord);
26741             return;
26742         }
26743         // clean styles
26744         if (node.className.length) {
26745             
26746             var cn = node.className.split(/\W+/);
26747             var cna = [];
26748             Roo.each(cn, function(cls) {
26749                 if (cls.match(/Mso[a-zA-Z]+/)) {
26750                     return;
26751                 }
26752                 cna.push(cls);
26753             });
26754             node.className = cna.length ? cna.join(' ') : '';
26755             if (!cna.length) {
26756                 node.removeAttribute("class");
26757             }
26758         }
26759         
26760         if (node.hasAttribute("lang")) {
26761             node.removeAttribute("lang");
26762         }
26763         
26764         if (node.hasAttribute("style")) {
26765             
26766             var styles = node.getAttribute("style").split(";");
26767             var nstyle = [];
26768             Roo.each(styles, function(s) {
26769                 if (!s.match(/:/)) {
26770                     return;
26771                 }
26772                 var kv = s.split(":");
26773                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26774                     return;
26775                 }
26776                 // what ever is left... we allow.
26777                 nstyle.push(s);
26778             });
26779             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26780             if (!nstyle.length) {
26781                 node.removeAttribute('style');
26782             }
26783         }
26784         this.iterateChildren(node, this.cleanWord);
26785         
26786         
26787         
26788     },
26789     /**
26790      * iterateChildren of a Node, calling fn each time, using this as the scole..
26791      * @param {DomNode} node node to iterate children of.
26792      * @param {Function} fn method of this class to call on each item.
26793      */
26794     iterateChildren : function(node, fn)
26795     {
26796         if (!node.childNodes.length) {
26797                 return;
26798         }
26799         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26800            fn.call(this, node.childNodes[i])
26801         }
26802     },
26803     
26804     
26805     /**
26806      * cleanTableWidths.
26807      *
26808      * Quite often pasting from word etc.. results in tables with column and widths.
26809      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26810      *
26811      */
26812     cleanTableWidths : function(node)
26813     {
26814          
26815          
26816         if (!node) {
26817             this.cleanTableWidths(this.doc.body);
26818             return;
26819         }
26820         
26821         // ignore list...
26822         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26823             return; 
26824         }
26825         Roo.log(node.tagName);
26826         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26827             this.iterateChildren(node, this.cleanTableWidths);
26828             return;
26829         }
26830         if (node.hasAttribute('width')) {
26831             node.removeAttribute('width');
26832         }
26833         
26834          
26835         if (node.hasAttribute("style")) {
26836             // pretty basic...
26837             
26838             var styles = node.getAttribute("style").split(";");
26839             var nstyle = [];
26840             Roo.each(styles, function(s) {
26841                 if (!s.match(/:/)) {
26842                     return;
26843                 }
26844                 var kv = s.split(":");
26845                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26846                     return;
26847                 }
26848                 // what ever is left... we allow.
26849                 nstyle.push(s);
26850             });
26851             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26852             if (!nstyle.length) {
26853                 node.removeAttribute('style');
26854             }
26855         }
26856         
26857         this.iterateChildren(node, this.cleanTableWidths);
26858         
26859         
26860     },
26861     
26862     
26863     
26864     
26865     domToHTML : function(currentElement, depth, nopadtext) {
26866         
26867         depth = depth || 0;
26868         nopadtext = nopadtext || false;
26869     
26870         if (!currentElement) {
26871             return this.domToHTML(this.doc.body);
26872         }
26873         
26874         //Roo.log(currentElement);
26875         var j;
26876         var allText = false;
26877         var nodeName = currentElement.nodeName;
26878         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26879         
26880         if  (nodeName == '#text') {
26881             
26882             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26883         }
26884         
26885         
26886         var ret = '';
26887         if (nodeName != 'BODY') {
26888              
26889             var i = 0;
26890             // Prints the node tagName, such as <A>, <IMG>, etc
26891             if (tagName) {
26892                 var attr = [];
26893                 for(i = 0; i < currentElement.attributes.length;i++) {
26894                     // quoting?
26895                     var aname = currentElement.attributes.item(i).name;
26896                     if (!currentElement.attributes.item(i).value.length) {
26897                         continue;
26898                     }
26899                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26900                 }
26901                 
26902                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26903             } 
26904             else {
26905                 
26906                 // eack
26907             }
26908         } else {
26909             tagName = false;
26910         }
26911         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26912             return ret;
26913         }
26914         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26915             nopadtext = true;
26916         }
26917         
26918         
26919         // Traverse the tree
26920         i = 0;
26921         var currentElementChild = currentElement.childNodes.item(i);
26922         var allText = true;
26923         var innerHTML  = '';
26924         lastnode = '';
26925         while (currentElementChild) {
26926             // Formatting code (indent the tree so it looks nice on the screen)
26927             var nopad = nopadtext;
26928             if (lastnode == 'SPAN') {
26929                 nopad  = true;
26930             }
26931             // text
26932             if  (currentElementChild.nodeName == '#text') {
26933                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26934                 toadd = nopadtext ? toadd : toadd.trim();
26935                 if (!nopad && toadd.length > 80) {
26936                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26937                 }
26938                 innerHTML  += toadd;
26939                 
26940                 i++;
26941                 currentElementChild = currentElement.childNodes.item(i);
26942                 lastNode = '';
26943                 continue;
26944             }
26945             allText = false;
26946             
26947             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26948                 
26949             // Recursively traverse the tree structure of the child node
26950             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26951             lastnode = currentElementChild.nodeName;
26952             i++;
26953             currentElementChild=currentElement.childNodes.item(i);
26954         }
26955         
26956         ret += innerHTML;
26957         
26958         if (!allText) {
26959                 // The remaining code is mostly for formatting the tree
26960             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26961         }
26962         
26963         
26964         if (tagName) {
26965             ret+= "</"+tagName+">";
26966         }
26967         return ret;
26968         
26969     },
26970         
26971     applyBlacklists : function()
26972     {
26973         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26974         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26975         
26976         this.white = [];
26977         this.black = [];
26978         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26979             if (b.indexOf(tag) > -1) {
26980                 return;
26981             }
26982             this.white.push(tag);
26983             
26984         }, this);
26985         
26986         Roo.each(w, function(tag) {
26987             if (b.indexOf(tag) > -1) {
26988                 return;
26989             }
26990             if (this.white.indexOf(tag) > -1) {
26991                 return;
26992             }
26993             this.white.push(tag);
26994             
26995         }, this);
26996         
26997         
26998         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26999             if (w.indexOf(tag) > -1) {
27000                 return;
27001             }
27002             this.black.push(tag);
27003             
27004         }, this);
27005         
27006         Roo.each(b, function(tag) {
27007             if (w.indexOf(tag) > -1) {
27008                 return;
27009             }
27010             if (this.black.indexOf(tag) > -1) {
27011                 return;
27012             }
27013             this.black.push(tag);
27014             
27015         }, this);
27016         
27017         
27018         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27019         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27020         
27021         this.cwhite = [];
27022         this.cblack = [];
27023         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27024             if (b.indexOf(tag) > -1) {
27025                 return;
27026             }
27027             this.cwhite.push(tag);
27028             
27029         }, this);
27030         
27031         Roo.each(w, function(tag) {
27032             if (b.indexOf(tag) > -1) {
27033                 return;
27034             }
27035             if (this.cwhite.indexOf(tag) > -1) {
27036                 return;
27037             }
27038             this.cwhite.push(tag);
27039             
27040         }, this);
27041         
27042         
27043         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27044             if (w.indexOf(tag) > -1) {
27045                 return;
27046             }
27047             this.cblack.push(tag);
27048             
27049         }, this);
27050         
27051         Roo.each(b, function(tag) {
27052             if (w.indexOf(tag) > -1) {
27053                 return;
27054             }
27055             if (this.cblack.indexOf(tag) > -1) {
27056                 return;
27057             }
27058             this.cblack.push(tag);
27059             
27060         }, this);
27061     },
27062     
27063     setStylesheets : function(stylesheets)
27064     {
27065         if(typeof(stylesheets) == 'string'){
27066             Roo.get(this.iframe.contentDocument.head).createChild({
27067                 tag : 'link',
27068                 rel : 'stylesheet',
27069                 type : 'text/css',
27070                 href : stylesheets
27071             });
27072             
27073             return;
27074         }
27075         var _this = this;
27076      
27077         Roo.each(stylesheets, function(s) {
27078             if(!s.length){
27079                 return;
27080             }
27081             
27082             Roo.get(_this.iframe.contentDocument.head).createChild({
27083                 tag : 'link',
27084                 rel : 'stylesheet',
27085                 type : 'text/css',
27086                 href : s
27087             });
27088         });
27089
27090         
27091     },
27092     
27093     removeStylesheets : function()
27094     {
27095         var _this = this;
27096         
27097         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27098             s.remove();
27099         });
27100     },
27101     
27102     setStyle : function(style)
27103     {
27104         Roo.get(this.iframe.contentDocument.head).createChild({
27105             tag : 'style',
27106             type : 'text/css',
27107             html : style
27108         });
27109
27110         return;
27111     }
27112     
27113     // hide stuff that is not compatible
27114     /**
27115      * @event blur
27116      * @hide
27117      */
27118     /**
27119      * @event change
27120      * @hide
27121      */
27122     /**
27123      * @event focus
27124      * @hide
27125      */
27126     /**
27127      * @event specialkey
27128      * @hide
27129      */
27130     /**
27131      * @cfg {String} fieldClass @hide
27132      */
27133     /**
27134      * @cfg {String} focusClass @hide
27135      */
27136     /**
27137      * @cfg {String} autoCreate @hide
27138      */
27139     /**
27140      * @cfg {String} inputType @hide
27141      */
27142     /**
27143      * @cfg {String} invalidClass @hide
27144      */
27145     /**
27146      * @cfg {String} invalidText @hide
27147      */
27148     /**
27149      * @cfg {String} msgFx @hide
27150      */
27151     /**
27152      * @cfg {String} validateOnBlur @hide
27153      */
27154 });
27155
27156 Roo.HtmlEditorCore.white = [
27157         'area', 'br', 'img', 'input', 'hr', 'wbr',
27158         
27159        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27160        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27161        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27162        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27163        'table',   'ul',         'xmp', 
27164        
27165        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27166       'thead',   'tr', 
27167      
27168       'dir', 'menu', 'ol', 'ul', 'dl',
27169        
27170       'embed',  'object'
27171 ];
27172
27173
27174 Roo.HtmlEditorCore.black = [
27175     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27176         'applet', // 
27177         'base',   'basefont', 'bgsound', 'blink',  'body', 
27178         'frame',  'frameset', 'head',    'html',   'ilayer', 
27179         'iframe', 'layer',  'link',     'meta',    'object',   
27180         'script', 'style' ,'title',  'xml' // clean later..
27181 ];
27182 Roo.HtmlEditorCore.clean = [
27183     'script', 'style', 'title', 'xml'
27184 ];
27185 Roo.HtmlEditorCore.remove = [
27186     'font'
27187 ];
27188 // attributes..
27189
27190 Roo.HtmlEditorCore.ablack = [
27191     'on'
27192 ];
27193     
27194 Roo.HtmlEditorCore.aclean = [ 
27195     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27196 ];
27197
27198 // protocols..
27199 Roo.HtmlEditorCore.pwhite= [
27200         'http',  'https',  'mailto'
27201 ];
27202
27203 // white listed style attributes.
27204 Roo.HtmlEditorCore.cwhite= [
27205       //  'text-align', /// default is to allow most things..
27206       
27207          
27208 //        'font-size'//??
27209 ];
27210
27211 // black listed style attributes.
27212 Roo.HtmlEditorCore.cblack= [
27213       //  'font-size' -- this can be set by the project 
27214 ];
27215
27216
27217 Roo.HtmlEditorCore.swapCodes   =[ 
27218     [    8211, "&#8211;" ], 
27219     [    8212, "&#8212;" ], 
27220     [    8216,  "'" ],  
27221     [    8217, "'" ],  
27222     [    8220, '"' ],  
27223     [    8221, '"' ],  
27224     [    8226, "*" ],  
27225     [    8230, "..." ]
27226 ]; 
27227
27228     /*
27229  * - LGPL
27230  *
27231  * HtmlEditor
27232  * 
27233  */
27234
27235 /**
27236  * @class Roo.bootstrap.HtmlEditor
27237  * @extends Roo.bootstrap.TextArea
27238  * Bootstrap HtmlEditor class
27239
27240  * @constructor
27241  * Create a new HtmlEditor
27242  * @param {Object} config The config object
27243  */
27244
27245 Roo.bootstrap.HtmlEditor = function(config){
27246     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27247     if (!this.toolbars) {
27248         this.toolbars = [];
27249     }
27250     
27251     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27252     this.addEvents({
27253             /**
27254              * @event initialize
27255              * Fires when the editor is fully initialized (including the iframe)
27256              * @param {HtmlEditor} this
27257              */
27258             initialize: true,
27259             /**
27260              * @event activate
27261              * Fires when the editor is first receives the focus. Any insertion must wait
27262              * until after this event.
27263              * @param {HtmlEditor} this
27264              */
27265             activate: true,
27266              /**
27267              * @event beforesync
27268              * Fires before the textarea is updated with content from the editor iframe. Return false
27269              * to cancel the sync.
27270              * @param {HtmlEditor} this
27271              * @param {String} html
27272              */
27273             beforesync: true,
27274              /**
27275              * @event beforepush
27276              * Fires before the iframe editor is updated with content from the textarea. Return false
27277              * to cancel the push.
27278              * @param {HtmlEditor} this
27279              * @param {String} html
27280              */
27281             beforepush: true,
27282              /**
27283              * @event sync
27284              * Fires when the textarea is updated with content from the editor iframe.
27285              * @param {HtmlEditor} this
27286              * @param {String} html
27287              */
27288             sync: true,
27289              /**
27290              * @event push
27291              * Fires when the iframe editor is updated with content from the textarea.
27292              * @param {HtmlEditor} this
27293              * @param {String} html
27294              */
27295             push: true,
27296              /**
27297              * @event editmodechange
27298              * Fires when the editor switches edit modes
27299              * @param {HtmlEditor} this
27300              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27301              */
27302             editmodechange: true,
27303             /**
27304              * @event editorevent
27305              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27306              * @param {HtmlEditor} this
27307              */
27308             editorevent: true,
27309             /**
27310              * @event firstfocus
27311              * Fires when on first focus - needed by toolbars..
27312              * @param {HtmlEditor} this
27313              */
27314             firstfocus: true,
27315             /**
27316              * @event autosave
27317              * Auto save the htmlEditor value as a file into Events
27318              * @param {HtmlEditor} this
27319              */
27320             autosave: true,
27321             /**
27322              * @event savedpreview
27323              * preview the saved version of htmlEditor
27324              * @param {HtmlEditor} this
27325              */
27326             savedpreview: true
27327         });
27328 };
27329
27330
27331 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27332     
27333     
27334       /**
27335      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27336      */
27337     toolbars : false,
27338     
27339      /**
27340     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27341     */
27342     btns : [],
27343    
27344      /**
27345      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27346      *                        Roo.resizable.
27347      */
27348     resizable : false,
27349      /**
27350      * @cfg {Number} height (in pixels)
27351      */   
27352     height: 300,
27353    /**
27354      * @cfg {Number} width (in pixels)
27355      */   
27356     width: false,
27357     
27358     /**
27359      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27360      * 
27361      */
27362     stylesheets: false,
27363     
27364     // id of frame..
27365     frameId: false,
27366     
27367     // private properties
27368     validationEvent : false,
27369     deferHeight: true,
27370     initialized : false,
27371     activated : false,
27372     
27373     onFocus : Roo.emptyFn,
27374     iframePad:3,
27375     hideMode:'offsets',
27376     
27377     tbContainer : false,
27378     
27379     bodyCls : '',
27380     
27381     toolbarContainer :function() {
27382         return this.wrap.select('.x-html-editor-tb',true).first();
27383     },
27384
27385     /**
27386      * Protected method that will not generally be called directly. It
27387      * is called when the editor creates its toolbar. Override this method if you need to
27388      * add custom toolbar buttons.
27389      * @param {HtmlEditor} editor
27390      */
27391     createToolbar : function(){
27392         Roo.log('renewing');
27393         Roo.log("create toolbars");
27394         
27395         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27396         this.toolbars[0].render(this.toolbarContainer());
27397         
27398         return;
27399         
27400 //        if (!editor.toolbars || !editor.toolbars.length) {
27401 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27402 //        }
27403 //        
27404 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27405 //            editor.toolbars[i] = Roo.factory(
27406 //                    typeof(editor.toolbars[i]) == 'string' ?
27407 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27408 //                Roo.bootstrap.HtmlEditor);
27409 //            editor.toolbars[i].init(editor);
27410 //        }
27411     },
27412
27413      
27414     // private
27415     onRender : function(ct, position)
27416     {
27417        // Roo.log("Call onRender: " + this.xtype);
27418         var _t = this;
27419         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27420       
27421         this.wrap = this.inputEl().wrap({
27422             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27423         });
27424         
27425         this.editorcore.onRender(ct, position);
27426          
27427         if (this.resizable) {
27428             this.resizeEl = new Roo.Resizable(this.wrap, {
27429                 pinned : true,
27430                 wrap: true,
27431                 dynamic : true,
27432                 minHeight : this.height,
27433                 height: this.height,
27434                 handles : this.resizable,
27435                 width: this.width,
27436                 listeners : {
27437                     resize : function(r, w, h) {
27438                         _t.onResize(w,h); // -something
27439                     }
27440                 }
27441             });
27442             
27443         }
27444         this.createToolbar(this);
27445        
27446         
27447         if(!this.width && this.resizable){
27448             this.setSize(this.wrap.getSize());
27449         }
27450         if (this.resizeEl) {
27451             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27452             // should trigger onReize..
27453         }
27454         
27455     },
27456
27457     // private
27458     onResize : function(w, h)
27459     {
27460         Roo.log('resize: ' +w + ',' + h );
27461         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27462         var ew = false;
27463         var eh = false;
27464         
27465         if(this.inputEl() ){
27466             if(typeof w == 'number'){
27467                 var aw = w - this.wrap.getFrameWidth('lr');
27468                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27469                 ew = aw;
27470             }
27471             if(typeof h == 'number'){
27472                  var tbh = -11;  // fixme it needs to tool bar size!
27473                 for (var i =0; i < this.toolbars.length;i++) {
27474                     // fixme - ask toolbars for heights?
27475                     tbh += this.toolbars[i].el.getHeight();
27476                     //if (this.toolbars[i].footer) {
27477                     //    tbh += this.toolbars[i].footer.el.getHeight();
27478                     //}
27479                 }
27480               
27481                 
27482                 
27483                 
27484                 
27485                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27486                 ah -= 5; // knock a few pixes off for look..
27487                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27488                 var eh = ah;
27489             }
27490         }
27491         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27492         this.editorcore.onResize(ew,eh);
27493         
27494     },
27495
27496     /**
27497      * Toggles the editor between standard and source edit mode.
27498      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27499      */
27500     toggleSourceEdit : function(sourceEditMode)
27501     {
27502         this.editorcore.toggleSourceEdit(sourceEditMode);
27503         
27504         if(this.editorcore.sourceEditMode){
27505             Roo.log('editor - showing textarea');
27506             
27507 //            Roo.log('in');
27508 //            Roo.log(this.syncValue());
27509             this.syncValue();
27510             this.inputEl().removeClass(['hide', 'x-hidden']);
27511             this.inputEl().dom.removeAttribute('tabIndex');
27512             this.inputEl().focus();
27513         }else{
27514             Roo.log('editor - hiding textarea');
27515 //            Roo.log('out')
27516 //            Roo.log(this.pushValue()); 
27517             this.pushValue();
27518             
27519             this.inputEl().addClass(['hide', 'x-hidden']);
27520             this.inputEl().dom.setAttribute('tabIndex', -1);
27521             //this.deferFocus();
27522         }
27523          
27524         if(this.resizable){
27525             this.setSize(this.wrap.getSize());
27526         }
27527         
27528         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27529     },
27530  
27531     // private (for BoxComponent)
27532     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27533
27534     // private (for BoxComponent)
27535     getResizeEl : function(){
27536         return this.wrap;
27537     },
27538
27539     // private (for BoxComponent)
27540     getPositionEl : function(){
27541         return this.wrap;
27542     },
27543
27544     // private
27545     initEvents : function(){
27546         this.originalValue = this.getValue();
27547     },
27548
27549 //    /**
27550 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27551 //     * @method
27552 //     */
27553 //    markInvalid : Roo.emptyFn,
27554 //    /**
27555 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27556 //     * @method
27557 //     */
27558 //    clearInvalid : Roo.emptyFn,
27559
27560     setValue : function(v){
27561         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27562         this.editorcore.pushValue();
27563     },
27564
27565      
27566     // private
27567     deferFocus : function(){
27568         this.focus.defer(10, this);
27569     },
27570
27571     // doc'ed in Field
27572     focus : function(){
27573         this.editorcore.focus();
27574         
27575     },
27576       
27577
27578     // private
27579     onDestroy : function(){
27580         
27581         
27582         
27583         if(this.rendered){
27584             
27585             for (var i =0; i < this.toolbars.length;i++) {
27586                 // fixme - ask toolbars for heights?
27587                 this.toolbars[i].onDestroy();
27588             }
27589             
27590             this.wrap.dom.innerHTML = '';
27591             this.wrap.remove();
27592         }
27593     },
27594
27595     // private
27596     onFirstFocus : function(){
27597         //Roo.log("onFirstFocus");
27598         this.editorcore.onFirstFocus();
27599          for (var i =0; i < this.toolbars.length;i++) {
27600             this.toolbars[i].onFirstFocus();
27601         }
27602         
27603     },
27604     
27605     // private
27606     syncValue : function()
27607     {   
27608         this.editorcore.syncValue();
27609     },
27610     
27611     pushValue : function()
27612     {   
27613         this.editorcore.pushValue();
27614     }
27615      
27616     
27617     // hide stuff that is not compatible
27618     /**
27619      * @event blur
27620      * @hide
27621      */
27622     /**
27623      * @event change
27624      * @hide
27625      */
27626     /**
27627      * @event focus
27628      * @hide
27629      */
27630     /**
27631      * @event specialkey
27632      * @hide
27633      */
27634     /**
27635      * @cfg {String} fieldClass @hide
27636      */
27637     /**
27638      * @cfg {String} focusClass @hide
27639      */
27640     /**
27641      * @cfg {String} autoCreate @hide
27642      */
27643     /**
27644      * @cfg {String} inputType @hide
27645      */
27646      
27647     /**
27648      * @cfg {String} invalidText @hide
27649      */
27650     /**
27651      * @cfg {String} msgFx @hide
27652      */
27653     /**
27654      * @cfg {String} validateOnBlur @hide
27655      */
27656 });
27657  
27658     
27659    
27660    
27661    
27662       
27663 Roo.namespace('Roo.bootstrap.htmleditor');
27664 /**
27665  * @class Roo.bootstrap.HtmlEditorToolbar1
27666  * Basic Toolbar
27667  * 
27668  * @example
27669  * Usage:
27670  *
27671  new Roo.bootstrap.HtmlEditor({
27672     ....
27673     toolbars : [
27674         new Roo.bootstrap.HtmlEditorToolbar1({
27675             disable : { fonts: 1 , format: 1, ..., ... , ...],
27676             btns : [ .... ]
27677         })
27678     }
27679      
27680  * 
27681  * @cfg {Object} disable List of elements to disable..
27682  * @cfg {Array} btns List of additional buttons.
27683  * 
27684  * 
27685  * NEEDS Extra CSS? 
27686  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27687  */
27688  
27689 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27690 {
27691     
27692     Roo.apply(this, config);
27693     
27694     // default disabled, based on 'good practice'..
27695     this.disable = this.disable || {};
27696     Roo.applyIf(this.disable, {
27697         fontSize : true,
27698         colors : true,
27699         specialElements : true
27700     });
27701     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27702     
27703     this.editor = config.editor;
27704     this.editorcore = config.editor.editorcore;
27705     
27706     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27707     
27708     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27709     // dont call parent... till later.
27710 }
27711 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27712      
27713     bar : true,
27714     
27715     editor : false,
27716     editorcore : false,
27717     
27718     
27719     formats : [
27720         "p" ,  
27721         "h1","h2","h3","h4","h5","h6", 
27722         "pre", "code", 
27723         "abbr", "acronym", "address", "cite", "samp", "var",
27724         'div','span'
27725     ],
27726     
27727     onRender : function(ct, position)
27728     {
27729        // Roo.log("Call onRender: " + this.xtype);
27730         
27731        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27732        Roo.log(this.el);
27733        this.el.dom.style.marginBottom = '0';
27734        var _this = this;
27735        var editorcore = this.editorcore;
27736        var editor= this.editor;
27737        
27738        var children = [];
27739        var btn = function(id,cmd , toggle, handler, html){
27740        
27741             var  event = toggle ? 'toggle' : 'click';
27742        
27743             var a = {
27744                 size : 'sm',
27745                 xtype: 'Button',
27746                 xns: Roo.bootstrap,
27747                 //glyphicon : id,
27748                 fa: id,
27749                 cmd : id || cmd,
27750                 enableToggle:toggle !== false,
27751                 html : html || '',
27752                 pressed : toggle ? false : null,
27753                 listeners : {}
27754             };
27755             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27756                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27757             };
27758             children.push(a);
27759             return a;
27760        }
27761        
27762     //    var cb_box = function...
27763         
27764         var style = {
27765                 xtype: 'Button',
27766                 size : 'sm',
27767                 xns: Roo.bootstrap,
27768                 fa : 'font',
27769                 //html : 'submit'
27770                 menu : {
27771                     xtype: 'Menu',
27772                     xns: Roo.bootstrap,
27773                     items:  []
27774                 }
27775         };
27776         Roo.each(this.formats, function(f) {
27777             style.menu.items.push({
27778                 xtype :'MenuItem',
27779                 xns: Roo.bootstrap,
27780                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27781                 tagname : f,
27782                 listeners : {
27783                     click : function()
27784                     {
27785                         editorcore.insertTag(this.tagname);
27786                         editor.focus();
27787                     }
27788                 }
27789                 
27790             });
27791         });
27792         children.push(style);   
27793         
27794         btn('bold',false,true);
27795         btn('italic',false,true);
27796         btn('align-left', 'justifyleft',true);
27797         btn('align-center', 'justifycenter',true);
27798         btn('align-right' , 'justifyright',true);
27799         btn('link', false, false, function(btn) {
27800             //Roo.log("create link?");
27801             var url = prompt(this.createLinkText, this.defaultLinkValue);
27802             if(url && url != 'http:/'+'/'){
27803                 this.editorcore.relayCmd('createlink', url);
27804             }
27805         }),
27806         btn('list','insertunorderedlist',true);
27807         btn('pencil', false,true, function(btn){
27808                 Roo.log(this);
27809                 this.toggleSourceEdit(btn.pressed);
27810         });
27811         
27812         if (this.editor.btns.length > 0) {
27813             for (var i = 0; i<this.editor.btns.length; i++) {
27814                 children.push(this.editor.btns[i]);
27815             }
27816         }
27817         
27818         /*
27819         var cog = {
27820                 xtype: 'Button',
27821                 size : 'sm',
27822                 xns: Roo.bootstrap,
27823                 glyphicon : 'cog',
27824                 //html : 'submit'
27825                 menu : {
27826                     xtype: 'Menu',
27827                     xns: Roo.bootstrap,
27828                     items:  []
27829                 }
27830         };
27831         
27832         cog.menu.items.push({
27833             xtype :'MenuItem',
27834             xns: Roo.bootstrap,
27835             html : Clean styles,
27836             tagname : f,
27837             listeners : {
27838                 click : function()
27839                 {
27840                     editorcore.insertTag(this.tagname);
27841                     editor.focus();
27842                 }
27843             }
27844             
27845         });
27846        */
27847         
27848          
27849        this.xtype = 'NavSimplebar';
27850         
27851         for(var i=0;i< children.length;i++) {
27852             
27853             this.buttons.add(this.addxtypeChild(children[i]));
27854             
27855         }
27856         
27857         editor.on('editorevent', this.updateToolbar, this);
27858     },
27859     onBtnClick : function(id)
27860     {
27861        this.editorcore.relayCmd(id);
27862        this.editorcore.focus();
27863     },
27864     
27865     /**
27866      * Protected method that will not generally be called directly. It triggers
27867      * a toolbar update by reading the markup state of the current selection in the editor.
27868      */
27869     updateToolbar: function(){
27870
27871         if(!this.editorcore.activated){
27872             this.editor.onFirstFocus(); // is this neeed?
27873             return;
27874         }
27875
27876         var btns = this.buttons; 
27877         var doc = this.editorcore.doc;
27878         btns.get('bold').setActive(doc.queryCommandState('bold'));
27879         btns.get('italic').setActive(doc.queryCommandState('italic'));
27880         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27881         
27882         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27883         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27884         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27885         
27886         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27887         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27888          /*
27889         
27890         var ans = this.editorcore.getAllAncestors();
27891         if (this.formatCombo) {
27892             
27893             
27894             var store = this.formatCombo.store;
27895             this.formatCombo.setValue("");
27896             for (var i =0; i < ans.length;i++) {
27897                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27898                     // select it..
27899                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27900                     break;
27901                 }
27902             }
27903         }
27904         
27905         
27906         
27907         // hides menus... - so this cant be on a menu...
27908         Roo.bootstrap.MenuMgr.hideAll();
27909         */
27910         Roo.bootstrap.MenuMgr.hideAll();
27911         //this.editorsyncValue();
27912     },
27913     onFirstFocus: function() {
27914         this.buttons.each(function(item){
27915            item.enable();
27916         });
27917     },
27918     toggleSourceEdit : function(sourceEditMode){
27919         
27920           
27921         if(sourceEditMode){
27922             Roo.log("disabling buttons");
27923            this.buttons.each( function(item){
27924                 if(item.cmd != 'pencil'){
27925                     item.disable();
27926                 }
27927             });
27928           
27929         }else{
27930             Roo.log("enabling buttons");
27931             if(this.editorcore.initialized){
27932                 this.buttons.each( function(item){
27933                     item.enable();
27934                 });
27935             }
27936             
27937         }
27938         Roo.log("calling toggole on editor");
27939         // tell the editor that it's been pressed..
27940         this.editor.toggleSourceEdit(sourceEditMode);
27941        
27942     }
27943 });
27944
27945
27946
27947
27948  
27949 /*
27950  * - LGPL
27951  */
27952
27953 /**
27954  * @class Roo.bootstrap.Markdown
27955  * @extends Roo.bootstrap.TextArea
27956  * Bootstrap Showdown editable area
27957  * @cfg {string} content
27958  * 
27959  * @constructor
27960  * Create a new Showdown
27961  */
27962
27963 Roo.bootstrap.Markdown = function(config){
27964     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27965    
27966 };
27967
27968 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27969     
27970     editing :false,
27971     
27972     initEvents : function()
27973     {
27974         
27975         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27976         this.markdownEl = this.el.createChild({
27977             cls : 'roo-markdown-area'
27978         });
27979         this.inputEl().addClass('d-none');
27980         if (this.getValue() == '') {
27981             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27982             
27983         } else {
27984             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27985         }
27986         this.markdownEl.on('click', this.toggleTextEdit, this);
27987         this.on('blur', this.toggleTextEdit, this);
27988         this.on('specialkey', this.resizeTextArea, this);
27989     },
27990     
27991     toggleTextEdit : function()
27992     {
27993         var sh = this.markdownEl.getHeight();
27994         this.inputEl().addClass('d-none');
27995         this.markdownEl.addClass('d-none');
27996         if (!this.editing) {
27997             // show editor?
27998             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27999             this.inputEl().removeClass('d-none');
28000             this.inputEl().focus();
28001             this.editing = true;
28002             return;
28003         }
28004         // show showdown...
28005         this.updateMarkdown();
28006         this.markdownEl.removeClass('d-none');
28007         this.editing = false;
28008         return;
28009     },
28010     updateMarkdown : function()
28011     {
28012         if (this.getValue() == '') {
28013             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28014             return;
28015         }
28016  
28017         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28018     },
28019     
28020     resizeTextArea: function () {
28021         
28022         var sh = 100;
28023         Roo.log([sh, this.getValue().split("\n").length * 30]);
28024         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28025     },
28026     setValue : function(val)
28027     {
28028         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28029         if (!this.editing) {
28030             this.updateMarkdown();
28031         }
28032         
28033     },
28034     focus : function()
28035     {
28036         if (!this.editing) {
28037             this.toggleTextEdit();
28038         }
28039         
28040     }
28041
28042
28043 });/*
28044  * Based on:
28045  * Ext JS Library 1.1.1
28046  * Copyright(c) 2006-2007, Ext JS, LLC.
28047  *
28048  * Originally Released Under LGPL - original licence link has changed is not relivant.
28049  *
28050  * Fork - LGPL
28051  * <script type="text/javascript">
28052  */
28053  
28054 /**
28055  * @class Roo.bootstrap.PagingToolbar
28056  * @extends Roo.bootstrap.NavSimplebar
28057  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28058  * @constructor
28059  * Create a new PagingToolbar
28060  * @param {Object} config The config object
28061  * @param {Roo.data.Store} store
28062  */
28063 Roo.bootstrap.PagingToolbar = function(config)
28064 {
28065     // old args format still supported... - xtype is prefered..
28066         // created from xtype...
28067     
28068     this.ds = config.dataSource;
28069     
28070     if (config.store && !this.ds) {
28071         this.store= Roo.factory(config.store, Roo.data);
28072         this.ds = this.store;
28073         this.ds.xmodule = this.xmodule || false;
28074     }
28075     
28076     this.toolbarItems = [];
28077     if (config.items) {
28078         this.toolbarItems = config.items;
28079     }
28080     
28081     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28082     
28083     this.cursor = 0;
28084     
28085     if (this.ds) { 
28086         this.bind(this.ds);
28087     }
28088     
28089     if (Roo.bootstrap.version == 4) {
28090         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28091     } else {
28092         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28093     }
28094     
28095 };
28096
28097 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28098     /**
28099      * @cfg {Roo.data.Store} dataSource
28100      * The underlying data store providing the paged data
28101      */
28102     /**
28103      * @cfg {String/HTMLElement/Element} container
28104      * container The id or element that will contain the toolbar
28105      */
28106     /**
28107      * @cfg {Boolean} displayInfo
28108      * True to display the displayMsg (defaults to false)
28109      */
28110     /**
28111      * @cfg {Number} pageSize
28112      * The number of records to display per page (defaults to 20)
28113      */
28114     pageSize: 20,
28115     /**
28116      * @cfg {String} displayMsg
28117      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28118      */
28119     displayMsg : 'Displaying {0} - {1} of {2}',
28120     /**
28121      * @cfg {String} emptyMsg
28122      * The message to display when no records are found (defaults to "No data to display")
28123      */
28124     emptyMsg : 'No data to display',
28125     /**
28126      * Customizable piece of the default paging text (defaults to "Page")
28127      * @type String
28128      */
28129     beforePageText : "Page",
28130     /**
28131      * Customizable piece of the default paging text (defaults to "of %0")
28132      * @type String
28133      */
28134     afterPageText : "of {0}",
28135     /**
28136      * Customizable piece of the default paging text (defaults to "First Page")
28137      * @type String
28138      */
28139     firstText : "First Page",
28140     /**
28141      * Customizable piece of the default paging text (defaults to "Previous Page")
28142      * @type String
28143      */
28144     prevText : "Previous Page",
28145     /**
28146      * Customizable piece of the default paging text (defaults to "Next Page")
28147      * @type String
28148      */
28149     nextText : "Next Page",
28150     /**
28151      * Customizable piece of the default paging text (defaults to "Last Page")
28152      * @type String
28153      */
28154     lastText : "Last Page",
28155     /**
28156      * Customizable piece of the default paging text (defaults to "Refresh")
28157      * @type String
28158      */
28159     refreshText : "Refresh",
28160
28161     buttons : false,
28162     // private
28163     onRender : function(ct, position) 
28164     {
28165         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28166         this.navgroup.parentId = this.id;
28167         this.navgroup.onRender(this.el, null);
28168         // add the buttons to the navgroup
28169         
28170         if(this.displayInfo){
28171             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28172             this.displayEl = this.el.select('.x-paging-info', true).first();
28173 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28174 //            this.displayEl = navel.el.select('span',true).first();
28175         }
28176         
28177         var _this = this;
28178         
28179         if(this.buttons){
28180             Roo.each(_this.buttons, function(e){ // this might need to use render????
28181                Roo.factory(e).render(_this.el);
28182             });
28183         }
28184             
28185         Roo.each(_this.toolbarItems, function(e) {
28186             _this.navgroup.addItem(e);
28187         });
28188         
28189         
28190         this.first = this.navgroup.addItem({
28191             tooltip: this.firstText,
28192             cls: "prev btn-outline-secondary",
28193             html : ' <i class="fa fa-step-backward"></i>',
28194             disabled: true,
28195             preventDefault: true,
28196             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28197         });
28198         
28199         this.prev =  this.navgroup.addItem({
28200             tooltip: this.prevText,
28201             cls: "prev btn-outline-secondary",
28202             html : ' <i class="fa fa-backward"></i>',
28203             disabled: true,
28204             preventDefault: true,
28205             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28206         });
28207     //this.addSeparator();
28208         
28209         
28210         var field = this.navgroup.addItem( {
28211             tagtype : 'span',
28212             cls : 'x-paging-position  btn-outline-secondary',
28213              disabled: true,
28214             html : this.beforePageText  +
28215                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28216                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28217          } ); //?? escaped?
28218         
28219         this.field = field.el.select('input', true).first();
28220         this.field.on("keydown", this.onPagingKeydown, this);
28221         this.field.on("focus", function(){this.dom.select();});
28222     
28223     
28224         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28225         //this.field.setHeight(18);
28226         //this.addSeparator();
28227         this.next = this.navgroup.addItem({
28228             tooltip: this.nextText,
28229             cls: "next btn-outline-secondary",
28230             html : ' <i class="fa fa-forward"></i>',
28231             disabled: true,
28232             preventDefault: true,
28233             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28234         });
28235         this.last = this.navgroup.addItem({
28236             tooltip: this.lastText,
28237             html : ' <i class="fa fa-step-forward"></i>',
28238             cls: "next btn-outline-secondary",
28239             disabled: true,
28240             preventDefault: true,
28241             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28242         });
28243     //this.addSeparator();
28244         this.loading = this.navgroup.addItem({
28245             tooltip: this.refreshText,
28246             cls: "btn-outline-secondary",
28247             html : ' <i class="fa fa-refresh"></i>',
28248             preventDefault: true,
28249             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28250         });
28251         
28252     },
28253
28254     // private
28255     updateInfo : function(){
28256         if(this.displayEl){
28257             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28258             var msg = count == 0 ?
28259                 this.emptyMsg :
28260                 String.format(
28261                     this.displayMsg,
28262                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28263                 );
28264             this.displayEl.update(msg);
28265         }
28266     },
28267
28268     // private
28269     onLoad : function(ds, r, o)
28270     {
28271         this.cursor = o.params && o.params.start ? o.params.start : 0;
28272         
28273         var d = this.getPageData(),
28274             ap = d.activePage,
28275             ps = d.pages;
28276         
28277         
28278         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28279         this.field.dom.value = ap;
28280         this.first.setDisabled(ap == 1);
28281         this.prev.setDisabled(ap == 1);
28282         this.next.setDisabled(ap == ps);
28283         this.last.setDisabled(ap == ps);
28284         this.loading.enable();
28285         this.updateInfo();
28286     },
28287
28288     // private
28289     getPageData : function(){
28290         var total = this.ds.getTotalCount();
28291         return {
28292             total : total,
28293             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28294             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28295         };
28296     },
28297
28298     // private
28299     onLoadError : function(){
28300         this.loading.enable();
28301     },
28302
28303     // private
28304     onPagingKeydown : function(e){
28305         var k = e.getKey();
28306         var d = this.getPageData();
28307         if(k == e.RETURN){
28308             var v = this.field.dom.value, pageNum;
28309             if(!v || isNaN(pageNum = parseInt(v, 10))){
28310                 this.field.dom.value = d.activePage;
28311                 return;
28312             }
28313             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28314             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28315             e.stopEvent();
28316         }
28317         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))
28318         {
28319           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28320           this.field.dom.value = pageNum;
28321           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28322           e.stopEvent();
28323         }
28324         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28325         {
28326           var v = this.field.dom.value, pageNum; 
28327           var increment = (e.shiftKey) ? 10 : 1;
28328           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28329                 increment *= -1;
28330           }
28331           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28332             this.field.dom.value = d.activePage;
28333             return;
28334           }
28335           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28336           {
28337             this.field.dom.value = parseInt(v, 10) + increment;
28338             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28340           }
28341           e.stopEvent();
28342         }
28343     },
28344
28345     // private
28346     beforeLoad : function(){
28347         if(this.loading){
28348             this.loading.disable();
28349         }
28350     },
28351
28352     // private
28353     onClick : function(which){
28354         
28355         var ds = this.ds;
28356         if (!ds) {
28357             return;
28358         }
28359         
28360         switch(which){
28361             case "first":
28362                 ds.load({params:{start: 0, limit: this.pageSize}});
28363             break;
28364             case "prev":
28365                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28366             break;
28367             case "next":
28368                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28369             break;
28370             case "last":
28371                 var total = ds.getTotalCount();
28372                 var extra = total % this.pageSize;
28373                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28374                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28375             break;
28376             case "refresh":
28377                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28378             break;
28379         }
28380     },
28381
28382     /**
28383      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28384      * @param {Roo.data.Store} store The data store to unbind
28385      */
28386     unbind : function(ds){
28387         ds.un("beforeload", this.beforeLoad, this);
28388         ds.un("load", this.onLoad, this);
28389         ds.un("loadexception", this.onLoadError, this);
28390         ds.un("remove", this.updateInfo, this);
28391         ds.un("add", this.updateInfo, this);
28392         this.ds = undefined;
28393     },
28394
28395     /**
28396      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28397      * @param {Roo.data.Store} store The data store to bind
28398      */
28399     bind : function(ds){
28400         ds.on("beforeload", this.beforeLoad, this);
28401         ds.on("load", this.onLoad, this);
28402         ds.on("loadexception", this.onLoadError, this);
28403         ds.on("remove", this.updateInfo, this);
28404         ds.on("add", this.updateInfo, this);
28405         this.ds = ds;
28406     }
28407 });/*
28408  * - LGPL
28409  *
28410  * element
28411  * 
28412  */
28413
28414 /**
28415  * @class Roo.bootstrap.MessageBar
28416  * @extends Roo.bootstrap.Component
28417  * Bootstrap MessageBar class
28418  * @cfg {String} html contents of the MessageBar
28419  * @cfg {String} weight (info | success | warning | danger) default info
28420  * @cfg {String} beforeClass insert the bar before the given class
28421  * @cfg {Boolean} closable (true | false) default false
28422  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28423  * 
28424  * @constructor
28425  * Create a new Element
28426  * @param {Object} config The config object
28427  */
28428
28429 Roo.bootstrap.MessageBar = function(config){
28430     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28431 };
28432
28433 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28434     
28435     html: '',
28436     weight: 'info',
28437     closable: false,
28438     fixed: false,
28439     beforeClass: 'bootstrap-sticky-wrap',
28440     
28441     getAutoCreate : function(){
28442         
28443         var cfg = {
28444             tag: 'div',
28445             cls: 'alert alert-dismissable alert-' + this.weight,
28446             cn: [
28447                 {
28448                     tag: 'span',
28449                     cls: 'message',
28450                     html: this.html || ''
28451                 }
28452             ]
28453         };
28454         
28455         if(this.fixed){
28456             cfg.cls += ' alert-messages-fixed';
28457         }
28458         
28459         if(this.closable){
28460             cfg.cn.push({
28461                 tag: 'button',
28462                 cls: 'close',
28463                 html: 'x'
28464             });
28465         }
28466         
28467         return cfg;
28468     },
28469     
28470     onRender : function(ct, position)
28471     {
28472         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28473         
28474         if(!this.el){
28475             var cfg = Roo.apply({},  this.getAutoCreate());
28476             cfg.id = Roo.id();
28477             
28478             if (this.cls) {
28479                 cfg.cls += ' ' + this.cls;
28480             }
28481             if (this.style) {
28482                 cfg.style = this.style;
28483             }
28484             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28485             
28486             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28487         }
28488         
28489         this.el.select('>button.close').on('click', this.hide, this);
28490         
28491     },
28492     
28493     show : function()
28494     {
28495         if (!this.rendered) {
28496             this.render();
28497         }
28498         
28499         this.el.show();
28500         
28501         this.fireEvent('show', this);
28502         
28503     },
28504     
28505     hide : function()
28506     {
28507         if (!this.rendered) {
28508             this.render();
28509         }
28510         
28511         this.el.hide();
28512         
28513         this.fireEvent('hide', this);
28514     },
28515     
28516     update : function()
28517     {
28518 //        var e = this.el.dom.firstChild;
28519 //        
28520 //        if(this.closable){
28521 //            e = e.nextSibling;
28522 //        }
28523 //        
28524 //        e.data = this.html || '';
28525
28526         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28527     }
28528    
28529 });
28530
28531  
28532
28533      /*
28534  * - LGPL
28535  *
28536  * Graph
28537  * 
28538  */
28539
28540
28541 /**
28542  * @class Roo.bootstrap.Graph
28543  * @extends Roo.bootstrap.Component
28544  * Bootstrap Graph class
28545 > Prameters
28546  -sm {number} sm 4
28547  -md {number} md 5
28548  @cfg {String} graphtype  bar | vbar | pie
28549  @cfg {number} g_x coodinator | centre x (pie)
28550  @cfg {number} g_y coodinator | centre y (pie)
28551  @cfg {number} g_r radius (pie)
28552  @cfg {number} g_height height of the chart (respected by all elements in the set)
28553  @cfg {number} g_width width of the chart (respected by all elements in the set)
28554  @cfg {Object} title The title of the chart
28555     
28556  -{Array}  values
28557  -opts (object) options for the chart 
28558      o {
28559      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28560      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28561      o vgutter (number)
28562      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.
28563      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28564      o to
28565      o stretch (boolean)
28566      o }
28567  -opts (object) options for the pie
28568      o{
28569      o cut
28570      o startAngle (number)
28571      o endAngle (number)
28572      } 
28573  *
28574  * @constructor
28575  * Create a new Input
28576  * @param {Object} config The config object
28577  */
28578
28579 Roo.bootstrap.Graph = function(config){
28580     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28581     
28582     this.addEvents({
28583         // img events
28584         /**
28585          * @event click
28586          * The img click event for the img.
28587          * @param {Roo.EventObject} e
28588          */
28589         "click" : true
28590     });
28591 };
28592
28593 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28594     
28595     sm: 4,
28596     md: 5,
28597     graphtype: 'bar',
28598     g_height: 250,
28599     g_width: 400,
28600     g_x: 50,
28601     g_y: 50,
28602     g_r: 30,
28603     opts:{
28604         //g_colors: this.colors,
28605         g_type: 'soft',
28606         g_gutter: '20%'
28607
28608     },
28609     title : false,
28610
28611     getAutoCreate : function(){
28612         
28613         var cfg = {
28614             tag: 'div',
28615             html : null
28616         };
28617         
28618         
28619         return  cfg;
28620     },
28621
28622     onRender : function(ct,position){
28623         
28624         
28625         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28626         
28627         if (typeof(Raphael) == 'undefined') {
28628             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28629             return;
28630         }
28631         
28632         this.raphael = Raphael(this.el.dom);
28633         
28634                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28635                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28636                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28637                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28638                 /*
28639                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28640                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28641                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28642                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28643                 
28644                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28645                 r.barchart(330, 10, 300, 220, data1);
28646                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28647                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28648                 */
28649                 
28650                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28651                 // r.barchart(30, 30, 560, 250,  xdata, {
28652                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28653                 //     axis : "0 0 1 1",
28654                 //     axisxlabels :  xdata
28655                 //     //yvalues : cols,
28656                    
28657                 // });
28658 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28659 //        
28660 //        this.load(null,xdata,{
28661 //                axis : "0 0 1 1",
28662 //                axisxlabels :  xdata
28663 //                });
28664
28665     },
28666
28667     load : function(graphtype,xdata,opts)
28668     {
28669         this.raphael.clear();
28670         if(!graphtype) {
28671             graphtype = this.graphtype;
28672         }
28673         if(!opts){
28674             opts = this.opts;
28675         }
28676         var r = this.raphael,
28677             fin = function () {
28678                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28679             },
28680             fout = function () {
28681                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28682             },
28683             pfin = function() {
28684                 this.sector.stop();
28685                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28686
28687                 if (this.label) {
28688                     this.label[0].stop();
28689                     this.label[0].attr({ r: 7.5 });
28690                     this.label[1].attr({ "font-weight": 800 });
28691                 }
28692             },
28693             pfout = function() {
28694                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28695
28696                 if (this.label) {
28697                     this.label[0].animate({ r: 5 }, 500, "bounce");
28698                     this.label[1].attr({ "font-weight": 400 });
28699                 }
28700             };
28701
28702         switch(graphtype){
28703             case 'bar':
28704                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28705                 break;
28706             case 'hbar':
28707                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28708                 break;
28709             case 'pie':
28710 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28711 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28712 //            
28713                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28714                 
28715                 break;
28716
28717         }
28718         
28719         if(this.title){
28720             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28721         }
28722         
28723     },
28724     
28725     setTitle: function(o)
28726     {
28727         this.title = o;
28728     },
28729     
28730     initEvents: function() {
28731         
28732         if(!this.href){
28733             this.el.on('click', this.onClick, this);
28734         }
28735     },
28736     
28737     onClick : function(e)
28738     {
28739         Roo.log('img onclick');
28740         this.fireEvent('click', this, e);
28741     }
28742    
28743 });
28744
28745  
28746 /*
28747  * - LGPL
28748  *
28749  * numberBox
28750  * 
28751  */
28752 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28753
28754 /**
28755  * @class Roo.bootstrap.dash.NumberBox
28756  * @extends Roo.bootstrap.Component
28757  * Bootstrap NumberBox class
28758  * @cfg {String} headline Box headline
28759  * @cfg {String} content Box content
28760  * @cfg {String} icon Box icon
28761  * @cfg {String} footer Footer text
28762  * @cfg {String} fhref Footer href
28763  * 
28764  * @constructor
28765  * Create a new NumberBox
28766  * @param {Object} config The config object
28767  */
28768
28769
28770 Roo.bootstrap.dash.NumberBox = function(config){
28771     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28772     
28773 };
28774
28775 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28776     
28777     headline : '',
28778     content : '',
28779     icon : '',
28780     footer : '',
28781     fhref : '',
28782     ficon : '',
28783     
28784     getAutoCreate : function(){
28785         
28786         var cfg = {
28787             tag : 'div',
28788             cls : 'small-box ',
28789             cn : [
28790                 {
28791                     tag : 'div',
28792                     cls : 'inner',
28793                     cn :[
28794                         {
28795                             tag : 'h3',
28796                             cls : 'roo-headline',
28797                             html : this.headline
28798                         },
28799                         {
28800                             tag : 'p',
28801                             cls : 'roo-content',
28802                             html : this.content
28803                         }
28804                     ]
28805                 }
28806             ]
28807         };
28808         
28809         if(this.icon){
28810             cfg.cn.push({
28811                 tag : 'div',
28812                 cls : 'icon',
28813                 cn :[
28814                     {
28815                         tag : 'i',
28816                         cls : 'ion ' + this.icon
28817                     }
28818                 ]
28819             });
28820         }
28821         
28822         if(this.footer){
28823             var footer = {
28824                 tag : 'a',
28825                 cls : 'small-box-footer',
28826                 href : this.fhref || '#',
28827                 html : this.footer
28828             };
28829             
28830             cfg.cn.push(footer);
28831             
28832         }
28833         
28834         return  cfg;
28835     },
28836
28837     onRender : function(ct,position){
28838         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28839
28840
28841        
28842                 
28843     },
28844
28845     setHeadline: function (value)
28846     {
28847         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28848     },
28849     
28850     setFooter: function (value, href)
28851     {
28852         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28853         
28854         if(href){
28855             this.el.select('a.small-box-footer',true).first().attr('href', href);
28856         }
28857         
28858     },
28859
28860     setContent: function (value)
28861     {
28862         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28863     },
28864
28865     initEvents: function() 
28866     {   
28867         
28868     }
28869     
28870 });
28871
28872  
28873 /*
28874  * - LGPL
28875  *
28876  * TabBox
28877  * 
28878  */
28879 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28880
28881 /**
28882  * @class Roo.bootstrap.dash.TabBox
28883  * @extends Roo.bootstrap.Component
28884  * Bootstrap TabBox class
28885  * @cfg {String} title Title of the TabBox
28886  * @cfg {String} icon Icon of the TabBox
28887  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28888  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28889  * 
28890  * @constructor
28891  * Create a new TabBox
28892  * @param {Object} config The config object
28893  */
28894
28895
28896 Roo.bootstrap.dash.TabBox = function(config){
28897     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28898     this.addEvents({
28899         // raw events
28900         /**
28901          * @event addpane
28902          * When a pane is added
28903          * @param {Roo.bootstrap.dash.TabPane} pane
28904          */
28905         "addpane" : true,
28906         /**
28907          * @event activatepane
28908          * When a pane is activated
28909          * @param {Roo.bootstrap.dash.TabPane} pane
28910          */
28911         "activatepane" : true
28912         
28913          
28914     });
28915     
28916     this.panes = [];
28917 };
28918
28919 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28920
28921     title : '',
28922     icon : false,
28923     showtabs : true,
28924     tabScrollable : false,
28925     
28926     getChildContainer : function()
28927     {
28928         return this.el.select('.tab-content', true).first();
28929     },
28930     
28931     getAutoCreate : function(){
28932         
28933         var header = {
28934             tag: 'li',
28935             cls: 'pull-left header',
28936             html: this.title,
28937             cn : []
28938         };
28939         
28940         if(this.icon){
28941             header.cn.push({
28942                 tag: 'i',
28943                 cls: 'fa ' + this.icon
28944             });
28945         }
28946         
28947         var h = {
28948             tag: 'ul',
28949             cls: 'nav nav-tabs pull-right',
28950             cn: [
28951                 header
28952             ]
28953         };
28954         
28955         if(this.tabScrollable){
28956             h = {
28957                 tag: 'div',
28958                 cls: 'tab-header',
28959                 cn: [
28960                     {
28961                         tag: 'ul',
28962                         cls: 'nav nav-tabs pull-right',
28963                         cn: [
28964                             header
28965                         ]
28966                     }
28967                 ]
28968             };
28969         }
28970         
28971         var cfg = {
28972             tag: 'div',
28973             cls: 'nav-tabs-custom',
28974             cn: [
28975                 h,
28976                 {
28977                     tag: 'div',
28978                     cls: 'tab-content no-padding',
28979                     cn: []
28980                 }
28981             ]
28982         };
28983
28984         return  cfg;
28985     },
28986     initEvents : function()
28987     {
28988         //Roo.log('add add pane handler');
28989         this.on('addpane', this.onAddPane, this);
28990     },
28991      /**
28992      * Updates the box title
28993      * @param {String} html to set the title to.
28994      */
28995     setTitle : function(value)
28996     {
28997         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28998     },
28999     onAddPane : function(pane)
29000     {
29001         this.panes.push(pane);
29002         //Roo.log('addpane');
29003         //Roo.log(pane);
29004         // tabs are rendere left to right..
29005         if(!this.showtabs){
29006             return;
29007         }
29008         
29009         var ctr = this.el.select('.nav-tabs', true).first();
29010          
29011          
29012         var existing = ctr.select('.nav-tab',true);
29013         var qty = existing.getCount();;
29014         
29015         
29016         var tab = ctr.createChild({
29017             tag : 'li',
29018             cls : 'nav-tab' + (qty ? '' : ' active'),
29019             cn : [
29020                 {
29021                     tag : 'a',
29022                     href:'#',
29023                     html : pane.title
29024                 }
29025             ]
29026         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29027         pane.tab = tab;
29028         
29029         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29030         if (!qty) {
29031             pane.el.addClass('active');
29032         }
29033         
29034                 
29035     },
29036     onTabClick : function(ev,un,ob,pane)
29037     {
29038         //Roo.log('tab - prev default');
29039         ev.preventDefault();
29040         
29041         
29042         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29043         pane.tab.addClass('active');
29044         //Roo.log(pane.title);
29045         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29046         // technically we should have a deactivate event.. but maybe add later.
29047         // and it should not de-activate the selected tab...
29048         this.fireEvent('activatepane', pane);
29049         pane.el.addClass('active');
29050         pane.fireEvent('activate');
29051         
29052         
29053     },
29054     
29055     getActivePane : function()
29056     {
29057         var r = false;
29058         Roo.each(this.panes, function(p) {
29059             if(p.el.hasClass('active')){
29060                 r = p;
29061                 return false;
29062             }
29063             
29064             return;
29065         });
29066         
29067         return r;
29068     }
29069     
29070     
29071 });
29072
29073  
29074 /*
29075  * - LGPL
29076  *
29077  * Tab pane
29078  * 
29079  */
29080 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29081 /**
29082  * @class Roo.bootstrap.TabPane
29083  * @extends Roo.bootstrap.Component
29084  * Bootstrap TabPane class
29085  * @cfg {Boolean} active (false | true) Default false
29086  * @cfg {String} title title of panel
29087
29088  * 
29089  * @constructor
29090  * Create a new TabPane
29091  * @param {Object} config The config object
29092  */
29093
29094 Roo.bootstrap.dash.TabPane = function(config){
29095     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29096     
29097     this.addEvents({
29098         // raw events
29099         /**
29100          * @event activate
29101          * When a pane is activated
29102          * @param {Roo.bootstrap.dash.TabPane} pane
29103          */
29104         "activate" : true
29105          
29106     });
29107 };
29108
29109 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29110     
29111     active : false,
29112     title : '',
29113     
29114     // the tabBox that this is attached to.
29115     tab : false,
29116      
29117     getAutoCreate : function() 
29118     {
29119         var cfg = {
29120             tag: 'div',
29121             cls: 'tab-pane'
29122         };
29123         
29124         if(this.active){
29125             cfg.cls += ' active';
29126         }
29127         
29128         return cfg;
29129     },
29130     initEvents  : function()
29131     {
29132         //Roo.log('trigger add pane handler');
29133         this.parent().fireEvent('addpane', this)
29134     },
29135     
29136      /**
29137      * Updates the tab title 
29138      * @param {String} html to set the title to.
29139      */
29140     setTitle: function(str)
29141     {
29142         if (!this.tab) {
29143             return;
29144         }
29145         this.title = str;
29146         this.tab.select('a', true).first().dom.innerHTML = str;
29147         
29148     }
29149     
29150     
29151     
29152 });
29153
29154  
29155
29156
29157  /*
29158  * - LGPL
29159  *
29160  * menu
29161  * 
29162  */
29163 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29164
29165 /**
29166  * @class Roo.bootstrap.menu.Menu
29167  * @extends Roo.bootstrap.Component
29168  * Bootstrap Menu class - container for Menu
29169  * @cfg {String} html Text of the menu
29170  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29171  * @cfg {String} icon Font awesome icon
29172  * @cfg {String} pos Menu align to (top | bottom) default bottom
29173  * 
29174  * 
29175  * @constructor
29176  * Create a new Menu
29177  * @param {Object} config The config object
29178  */
29179
29180
29181 Roo.bootstrap.menu.Menu = function(config){
29182     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29183     
29184     this.addEvents({
29185         /**
29186          * @event beforeshow
29187          * Fires before this menu is displayed
29188          * @param {Roo.bootstrap.menu.Menu} this
29189          */
29190         beforeshow : true,
29191         /**
29192          * @event beforehide
29193          * Fires before this menu is hidden
29194          * @param {Roo.bootstrap.menu.Menu} this
29195          */
29196         beforehide : true,
29197         /**
29198          * @event show
29199          * Fires after this menu is displayed
29200          * @param {Roo.bootstrap.menu.Menu} this
29201          */
29202         show : true,
29203         /**
29204          * @event hide
29205          * Fires after this menu is hidden
29206          * @param {Roo.bootstrap.menu.Menu} this
29207          */
29208         hide : true,
29209         /**
29210          * @event click
29211          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29212          * @param {Roo.bootstrap.menu.Menu} this
29213          * @param {Roo.EventObject} e
29214          */
29215         click : true
29216     });
29217     
29218 };
29219
29220 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29221     
29222     submenu : false,
29223     html : '',
29224     weight : 'default',
29225     icon : false,
29226     pos : 'bottom',
29227     
29228     
29229     getChildContainer : function() {
29230         if(this.isSubMenu){
29231             return this.el;
29232         }
29233         
29234         return this.el.select('ul.dropdown-menu', true).first();  
29235     },
29236     
29237     getAutoCreate : function()
29238     {
29239         var text = [
29240             {
29241                 tag : 'span',
29242                 cls : 'roo-menu-text',
29243                 html : this.html
29244             }
29245         ];
29246         
29247         if(this.icon){
29248             text.unshift({
29249                 tag : 'i',
29250                 cls : 'fa ' + this.icon
29251             })
29252         }
29253         
29254         
29255         var cfg = {
29256             tag : 'div',
29257             cls : 'btn-group',
29258             cn : [
29259                 {
29260                     tag : 'button',
29261                     cls : 'dropdown-button btn btn-' + this.weight,
29262                     cn : text
29263                 },
29264                 {
29265                     tag : 'button',
29266                     cls : 'dropdown-toggle btn btn-' + this.weight,
29267                     cn : [
29268                         {
29269                             tag : 'span',
29270                             cls : 'caret'
29271                         }
29272                     ]
29273                 },
29274                 {
29275                     tag : 'ul',
29276                     cls : 'dropdown-menu'
29277                 }
29278             ]
29279             
29280         };
29281         
29282         if(this.pos == 'top'){
29283             cfg.cls += ' dropup';
29284         }
29285         
29286         if(this.isSubMenu){
29287             cfg = {
29288                 tag : 'ul',
29289                 cls : 'dropdown-menu'
29290             }
29291         }
29292         
29293         return cfg;
29294     },
29295     
29296     onRender : function(ct, position)
29297     {
29298         this.isSubMenu = ct.hasClass('dropdown-submenu');
29299         
29300         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29301     },
29302     
29303     initEvents : function() 
29304     {
29305         if(this.isSubMenu){
29306             return;
29307         }
29308         
29309         this.hidden = true;
29310         
29311         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29312         this.triggerEl.on('click', this.onTriggerPress, this);
29313         
29314         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29315         this.buttonEl.on('click', this.onClick, this);
29316         
29317     },
29318     
29319     list : function()
29320     {
29321         if(this.isSubMenu){
29322             return this.el;
29323         }
29324         
29325         return this.el.select('ul.dropdown-menu', true).first();
29326     },
29327     
29328     onClick : function(e)
29329     {
29330         this.fireEvent("click", this, e);
29331     },
29332     
29333     onTriggerPress  : function(e)
29334     {   
29335         if (this.isVisible()) {
29336             this.hide();
29337         } else {
29338             this.show();
29339         }
29340     },
29341     
29342     isVisible : function(){
29343         return !this.hidden;
29344     },
29345     
29346     show : function()
29347     {
29348         this.fireEvent("beforeshow", this);
29349         
29350         this.hidden = false;
29351         this.el.addClass('open');
29352         
29353         Roo.get(document).on("mouseup", this.onMouseUp, this);
29354         
29355         this.fireEvent("show", this);
29356         
29357         
29358     },
29359     
29360     hide : function()
29361     {
29362         this.fireEvent("beforehide", this);
29363         
29364         this.hidden = true;
29365         this.el.removeClass('open');
29366         
29367         Roo.get(document).un("mouseup", this.onMouseUp);
29368         
29369         this.fireEvent("hide", this);
29370     },
29371     
29372     onMouseUp : function()
29373     {
29374         this.hide();
29375     }
29376     
29377 });
29378
29379  
29380  /*
29381  * - LGPL
29382  *
29383  * menu item
29384  * 
29385  */
29386 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29387
29388 /**
29389  * @class Roo.bootstrap.menu.Item
29390  * @extends Roo.bootstrap.Component
29391  * Bootstrap MenuItem class
29392  * @cfg {Boolean} submenu (true | false) default false
29393  * @cfg {String} html text of the item
29394  * @cfg {String} href the link
29395  * @cfg {Boolean} disable (true | false) default false
29396  * @cfg {Boolean} preventDefault (true | false) default true
29397  * @cfg {String} icon Font awesome icon
29398  * @cfg {String} pos Submenu align to (left | right) default right 
29399  * 
29400  * 
29401  * @constructor
29402  * Create a new Item
29403  * @param {Object} config The config object
29404  */
29405
29406
29407 Roo.bootstrap.menu.Item = function(config){
29408     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29409     this.addEvents({
29410         /**
29411          * @event mouseover
29412          * Fires when the mouse is hovering over this menu
29413          * @param {Roo.bootstrap.menu.Item} this
29414          * @param {Roo.EventObject} e
29415          */
29416         mouseover : true,
29417         /**
29418          * @event mouseout
29419          * Fires when the mouse exits this menu
29420          * @param {Roo.bootstrap.menu.Item} this
29421          * @param {Roo.EventObject} e
29422          */
29423         mouseout : true,
29424         // raw events
29425         /**
29426          * @event click
29427          * The raw click event for the entire grid.
29428          * @param {Roo.EventObject} e
29429          */
29430         click : true
29431     });
29432 };
29433
29434 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29435     
29436     submenu : false,
29437     href : '',
29438     html : '',
29439     preventDefault: true,
29440     disable : false,
29441     icon : false,
29442     pos : 'right',
29443     
29444     getAutoCreate : function()
29445     {
29446         var text = [
29447             {
29448                 tag : 'span',
29449                 cls : 'roo-menu-item-text',
29450                 html : this.html
29451             }
29452         ];
29453         
29454         if(this.icon){
29455             text.unshift({
29456                 tag : 'i',
29457                 cls : 'fa ' + this.icon
29458             })
29459         }
29460         
29461         var cfg = {
29462             tag : 'li',
29463             cn : [
29464                 {
29465                     tag : 'a',
29466                     href : this.href || '#',
29467                     cn : text
29468                 }
29469             ]
29470         };
29471         
29472         if(this.disable){
29473             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29474         }
29475         
29476         if(this.submenu){
29477             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29478             
29479             if(this.pos == 'left'){
29480                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29481             }
29482         }
29483         
29484         return cfg;
29485     },
29486     
29487     initEvents : function() 
29488     {
29489         this.el.on('mouseover', this.onMouseOver, this);
29490         this.el.on('mouseout', this.onMouseOut, this);
29491         
29492         this.el.select('a', true).first().on('click', this.onClick, this);
29493         
29494     },
29495     
29496     onClick : function(e)
29497     {
29498         if(this.preventDefault){
29499             e.preventDefault();
29500         }
29501         
29502         this.fireEvent("click", this, e);
29503     },
29504     
29505     onMouseOver : function(e)
29506     {
29507         if(this.submenu && this.pos == 'left'){
29508             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29509         }
29510         
29511         this.fireEvent("mouseover", this, e);
29512     },
29513     
29514     onMouseOut : function(e)
29515     {
29516         this.fireEvent("mouseout", this, e);
29517     }
29518 });
29519
29520  
29521
29522  /*
29523  * - LGPL
29524  *
29525  * menu separator
29526  * 
29527  */
29528 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29529
29530 /**
29531  * @class Roo.bootstrap.menu.Separator
29532  * @extends Roo.bootstrap.Component
29533  * Bootstrap Separator class
29534  * 
29535  * @constructor
29536  * Create a new Separator
29537  * @param {Object} config The config object
29538  */
29539
29540
29541 Roo.bootstrap.menu.Separator = function(config){
29542     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29543 };
29544
29545 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29546     
29547     getAutoCreate : function(){
29548         var cfg = {
29549             tag : 'li',
29550             cls: 'dropdown-divider divider'
29551         };
29552         
29553         return cfg;
29554     }
29555    
29556 });
29557
29558  
29559
29560  /*
29561  * - LGPL
29562  *
29563  * Tooltip
29564  * 
29565  */
29566
29567 /**
29568  * @class Roo.bootstrap.Tooltip
29569  * Bootstrap Tooltip class
29570  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29571  * to determine which dom element triggers the tooltip.
29572  * 
29573  * It needs to add support for additional attributes like tooltip-position
29574  * 
29575  * @constructor
29576  * Create a new Toolti
29577  * @param {Object} config The config object
29578  */
29579
29580 Roo.bootstrap.Tooltip = function(config){
29581     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29582     
29583     this.alignment = Roo.bootstrap.Tooltip.alignment;
29584     
29585     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29586         this.alignment = config.alignment;
29587     }
29588     
29589 };
29590
29591 Roo.apply(Roo.bootstrap.Tooltip, {
29592     /**
29593      * @function init initialize tooltip monitoring.
29594      * @static
29595      */
29596     currentEl : false,
29597     currentTip : false,
29598     currentRegion : false,
29599     
29600     //  init : delay?
29601     
29602     init : function()
29603     {
29604         Roo.get(document).on('mouseover', this.enter ,this);
29605         Roo.get(document).on('mouseout', this.leave, this);
29606          
29607         
29608         this.currentTip = new Roo.bootstrap.Tooltip();
29609     },
29610     
29611     enter : function(ev)
29612     {
29613         var dom = ev.getTarget();
29614         
29615         //Roo.log(['enter',dom]);
29616         var el = Roo.fly(dom);
29617         if (this.currentEl) {
29618             //Roo.log(dom);
29619             //Roo.log(this.currentEl);
29620             //Roo.log(this.currentEl.contains(dom));
29621             if (this.currentEl == el) {
29622                 return;
29623             }
29624             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29625                 return;
29626             }
29627
29628         }
29629         
29630         if (this.currentTip.el) {
29631             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29632         }    
29633         //Roo.log(ev);
29634         
29635         if(!el || el.dom == document){
29636             return;
29637         }
29638         
29639         var bindEl = el; 
29640         var pel = false;
29641         if (!el.attr('tooltip')) {
29642             pel = el.findParent("[tooltip]");
29643             if (pel) {
29644                 bindEl = Roo.get(pel);
29645             }
29646         }
29647         
29648        
29649         
29650         // you can not look for children, as if el is the body.. then everythign is the child..
29651         if (!pel && !el.attr('tooltip')) { //
29652             if (!el.select("[tooltip]").elements.length) {
29653                 return;
29654             }
29655             // is the mouse over this child...?
29656             bindEl = el.select("[tooltip]").first();
29657             var xy = ev.getXY();
29658             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29659                 //Roo.log("not in region.");
29660                 return;
29661             }
29662             //Roo.log("child element over..");
29663             
29664         }
29665         this.currentEl = el;
29666         this.currentTip.bind(bindEl);
29667         this.currentRegion = Roo.lib.Region.getRegion(dom);
29668         this.currentTip.enter();
29669         
29670     },
29671     leave : function(ev)
29672     {
29673         var dom = ev.getTarget();
29674         //Roo.log(['leave',dom]);
29675         if (!this.currentEl) {
29676             return;
29677         }
29678         
29679         
29680         if (dom != this.currentEl.dom) {
29681             return;
29682         }
29683         var xy = ev.getXY();
29684         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29685             return;
29686         }
29687         // only activate leave if mouse cursor is outside... bounding box..
29688         
29689         
29690         
29691         
29692         if (this.currentTip) {
29693             this.currentTip.leave();
29694         }
29695         //Roo.log('clear currentEl');
29696         this.currentEl = false;
29697         
29698         
29699     },
29700     alignment : {
29701         'left' : ['r-l', [-2,0], 'right'],
29702         'right' : ['l-r', [2,0], 'left'],
29703         'bottom' : ['t-b', [0,2], 'top'],
29704         'top' : [ 'b-t', [0,-2], 'bottom']
29705     }
29706     
29707 });
29708
29709
29710 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29711     
29712     
29713     bindEl : false,
29714     
29715     delay : null, // can be { show : 300 , hide: 500}
29716     
29717     timeout : null,
29718     
29719     hoverState : null, //???
29720     
29721     placement : 'bottom', 
29722     
29723     alignment : false,
29724     
29725     getAutoCreate : function(){
29726     
29727         var cfg = {
29728            cls : 'tooltip',   
29729            role : 'tooltip',
29730            cn : [
29731                 {
29732                     cls : 'tooltip-arrow arrow'
29733                 },
29734                 {
29735                     cls : 'tooltip-inner'
29736                 }
29737            ]
29738         };
29739         
29740         return cfg;
29741     },
29742     bind : function(el)
29743     {
29744         this.bindEl = el;
29745     },
29746     
29747     initEvents : function()
29748     {
29749         this.arrowEl = this.el.select('.arrow', true).first();
29750         this.innerEl = this.el.select('.tooltip-inner', true).first();
29751     },
29752     
29753     enter : function () {
29754        
29755         if (this.timeout != null) {
29756             clearTimeout(this.timeout);
29757         }
29758         
29759         this.hoverState = 'in';
29760          //Roo.log("enter - show");
29761         if (!this.delay || !this.delay.show) {
29762             this.show();
29763             return;
29764         }
29765         var _t = this;
29766         this.timeout = setTimeout(function () {
29767             if (_t.hoverState == 'in') {
29768                 _t.show();
29769             }
29770         }, this.delay.show);
29771     },
29772     leave : function()
29773     {
29774         clearTimeout(this.timeout);
29775     
29776         this.hoverState = 'out';
29777          if (!this.delay || !this.delay.hide) {
29778             this.hide();
29779             return;
29780         }
29781        
29782         var _t = this;
29783         this.timeout = setTimeout(function () {
29784             //Roo.log("leave - timeout");
29785             
29786             if (_t.hoverState == 'out') {
29787                 _t.hide();
29788                 Roo.bootstrap.Tooltip.currentEl = false;
29789             }
29790         }, delay);
29791     },
29792     
29793     show : function (msg)
29794     {
29795         if (!this.el) {
29796             this.render(document.body);
29797         }
29798         // set content.
29799         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29800         
29801         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29802         
29803         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29804         
29805         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29806                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29807         
29808         var placement = typeof this.placement == 'function' ?
29809             this.placement.call(this, this.el, on_el) :
29810             this.placement;
29811             
29812         var autoToken = /\s?auto?\s?/i;
29813         var autoPlace = autoToken.test(placement);
29814         if (autoPlace) {
29815             placement = placement.replace(autoToken, '') || 'top';
29816         }
29817         
29818         //this.el.detach()
29819         //this.el.setXY([0,0]);
29820         this.el.show();
29821         //this.el.dom.style.display='block';
29822         
29823         //this.el.appendTo(on_el);
29824         
29825         var p = this.getPosition();
29826         var box = this.el.getBox();
29827         
29828         if (autoPlace) {
29829             // fixme..
29830         }
29831         
29832         var align = this.alignment[placement];
29833         
29834         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29835         
29836         if(placement == 'top' || placement == 'bottom'){
29837             if(xy[0] < 0){
29838                 placement = 'right';
29839             }
29840             
29841             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29842                 placement = 'left';
29843             }
29844             
29845             var scroll = Roo.select('body', true).first().getScroll();
29846             
29847             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29848                 placement = 'top';
29849             }
29850             
29851             align = this.alignment[placement];
29852             
29853             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29854             
29855         }
29856         
29857         var elems = document.getElementsByTagName('div');
29858         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29859         for (var i = 0; i < elems.length; i++) {
29860           var zindex = Number.parseInt(
29861                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29862                 10
29863           );
29864           if (zindex > highest) {
29865             highest = zindex;
29866           }
29867         }
29868         
29869         
29870         
29871         this.el.dom.style.zIndex = highest;
29872         
29873         this.el.alignTo(this.bindEl, align[0],align[1]);
29874         //var arrow = this.el.select('.arrow',true).first();
29875         //arrow.set(align[2], 
29876         
29877         this.el.addClass(placement);
29878         this.el.addClass("bs-tooltip-"+ placement);
29879         
29880         this.el.addClass('in fade show');
29881         
29882         this.hoverState = null;
29883         
29884         if (this.el.hasClass('fade')) {
29885             // fade it?
29886         }
29887         
29888         
29889         
29890         
29891         
29892     },
29893     hide : function()
29894     {
29895          
29896         if (!this.el) {
29897             return;
29898         }
29899         //this.el.setXY([0,0]);
29900         this.el.removeClass(['show', 'in']);
29901         //this.el.hide();
29902         
29903     }
29904     
29905 });
29906  
29907
29908  /*
29909  * - LGPL
29910  *
29911  * Location Picker
29912  * 
29913  */
29914
29915 /**
29916  * @class Roo.bootstrap.LocationPicker
29917  * @extends Roo.bootstrap.Component
29918  * Bootstrap LocationPicker class
29919  * @cfg {Number} latitude Position when init default 0
29920  * @cfg {Number} longitude Position when init default 0
29921  * @cfg {Number} zoom default 15
29922  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29923  * @cfg {Boolean} mapTypeControl default false
29924  * @cfg {Boolean} disableDoubleClickZoom default false
29925  * @cfg {Boolean} scrollwheel default true
29926  * @cfg {Boolean} streetViewControl default false
29927  * @cfg {Number} radius default 0
29928  * @cfg {String} locationName
29929  * @cfg {Boolean} draggable default true
29930  * @cfg {Boolean} enableAutocomplete default false
29931  * @cfg {Boolean} enableReverseGeocode default true
29932  * @cfg {String} markerTitle
29933  * 
29934  * @constructor
29935  * Create a new LocationPicker
29936  * @param {Object} config The config object
29937  */
29938
29939
29940 Roo.bootstrap.LocationPicker = function(config){
29941     
29942     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29943     
29944     this.addEvents({
29945         /**
29946          * @event initial
29947          * Fires when the picker initialized.
29948          * @param {Roo.bootstrap.LocationPicker} this
29949          * @param {Google Location} location
29950          */
29951         initial : true,
29952         /**
29953          * @event positionchanged
29954          * Fires when the picker position changed.
29955          * @param {Roo.bootstrap.LocationPicker} this
29956          * @param {Google Location} location
29957          */
29958         positionchanged : true,
29959         /**
29960          * @event resize
29961          * Fires when the map resize.
29962          * @param {Roo.bootstrap.LocationPicker} this
29963          */
29964         resize : true,
29965         /**
29966          * @event show
29967          * Fires when the map show.
29968          * @param {Roo.bootstrap.LocationPicker} this
29969          */
29970         show : true,
29971         /**
29972          * @event hide
29973          * Fires when the map hide.
29974          * @param {Roo.bootstrap.LocationPicker} this
29975          */
29976         hide : true,
29977         /**
29978          * @event mapClick
29979          * Fires when click the map.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          * @param {Map event} e
29982          */
29983         mapClick : true,
29984         /**
29985          * @event mapRightClick
29986          * Fires when right click the map.
29987          * @param {Roo.bootstrap.LocationPicker} this
29988          * @param {Map event} e
29989          */
29990         mapRightClick : true,
29991         /**
29992          * @event markerClick
29993          * Fires when click the marker.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          * @param {Map event} e
29996          */
29997         markerClick : true,
29998         /**
29999          * @event markerRightClick
30000          * Fires when right click the marker.
30001          * @param {Roo.bootstrap.LocationPicker} this
30002          * @param {Map event} e
30003          */
30004         markerRightClick : true,
30005         /**
30006          * @event OverlayViewDraw
30007          * Fires when OverlayView Draw
30008          * @param {Roo.bootstrap.LocationPicker} this
30009          */
30010         OverlayViewDraw : true,
30011         /**
30012          * @event OverlayViewOnAdd
30013          * Fires when OverlayView Draw
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          */
30016         OverlayViewOnAdd : true,
30017         /**
30018          * @event OverlayViewOnRemove
30019          * Fires when OverlayView Draw
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          */
30022         OverlayViewOnRemove : true,
30023         /**
30024          * @event OverlayViewShow
30025          * Fires when OverlayView Draw
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Pixel} cpx
30028          */
30029         OverlayViewShow : true,
30030         /**
30031          * @event OverlayViewHide
30032          * Fires when OverlayView Draw
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         OverlayViewHide : true,
30036         /**
30037          * @event loadexception
30038          * Fires when load google lib failed.
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          */
30041         loadexception : true
30042     });
30043         
30044 };
30045
30046 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30047     
30048     gMapContext: false,
30049     
30050     latitude: 0,
30051     longitude: 0,
30052     zoom: 15,
30053     mapTypeId: false,
30054     mapTypeControl: false,
30055     disableDoubleClickZoom: false,
30056     scrollwheel: true,
30057     streetViewControl: false,
30058     radius: 0,
30059     locationName: '',
30060     draggable: true,
30061     enableAutocomplete: false,
30062     enableReverseGeocode: true,
30063     markerTitle: '',
30064     
30065     getAutoCreate: function()
30066     {
30067
30068         var cfg = {
30069             tag: 'div',
30070             cls: 'roo-location-picker'
30071         };
30072         
30073         return cfg
30074     },
30075     
30076     initEvents: function(ct, position)
30077     {       
30078         if(!this.el.getWidth() || this.isApplied()){
30079             return;
30080         }
30081         
30082         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30083         
30084         this.initial();
30085     },
30086     
30087     initial: function()
30088     {
30089         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30090             this.fireEvent('loadexception', this);
30091             return;
30092         }
30093         
30094         if(!this.mapTypeId){
30095             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30096         }
30097         
30098         this.gMapContext = this.GMapContext();
30099         
30100         this.initOverlayView();
30101         
30102         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30103         
30104         var _this = this;
30105                 
30106         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30107             _this.setPosition(_this.gMapContext.marker.position);
30108         });
30109         
30110         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30111             _this.fireEvent('mapClick', this, event);
30112             
30113         });
30114
30115         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30116             _this.fireEvent('mapRightClick', this, event);
30117             
30118         });
30119         
30120         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30121             _this.fireEvent('markerClick', this, event);
30122             
30123         });
30124
30125         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30126             _this.fireEvent('markerRightClick', this, event);
30127             
30128         });
30129         
30130         this.setPosition(this.gMapContext.location);
30131         
30132         this.fireEvent('initial', this, this.gMapContext.location);
30133     },
30134     
30135     initOverlayView: function()
30136     {
30137         var _this = this;
30138         
30139         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30140             
30141             draw: function()
30142             {
30143                 _this.fireEvent('OverlayViewDraw', _this);
30144             },
30145             
30146             onAdd: function()
30147             {
30148                 _this.fireEvent('OverlayViewOnAdd', _this);
30149             },
30150             
30151             onRemove: function()
30152             {
30153                 _this.fireEvent('OverlayViewOnRemove', _this);
30154             },
30155             
30156             show: function(cpx)
30157             {
30158                 _this.fireEvent('OverlayViewShow', _this, cpx);
30159             },
30160             
30161             hide: function()
30162             {
30163                 _this.fireEvent('OverlayViewHide', _this);
30164             }
30165             
30166         });
30167     },
30168     
30169     fromLatLngToContainerPixel: function(event)
30170     {
30171         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30172     },
30173     
30174     isApplied: function() 
30175     {
30176         return this.getGmapContext() == false ? false : true;
30177     },
30178     
30179     getGmapContext: function() 
30180     {
30181         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30182     },
30183     
30184     GMapContext: function() 
30185     {
30186         var position = new google.maps.LatLng(this.latitude, this.longitude);
30187         
30188         var _map = new google.maps.Map(this.el.dom, {
30189             center: position,
30190             zoom: this.zoom,
30191             mapTypeId: this.mapTypeId,
30192             mapTypeControl: this.mapTypeControl,
30193             disableDoubleClickZoom: this.disableDoubleClickZoom,
30194             scrollwheel: this.scrollwheel,
30195             streetViewControl: this.streetViewControl,
30196             locationName: this.locationName,
30197             draggable: this.draggable,
30198             enableAutocomplete: this.enableAutocomplete,
30199             enableReverseGeocode: this.enableReverseGeocode
30200         });
30201         
30202         var _marker = new google.maps.Marker({
30203             position: position,
30204             map: _map,
30205             title: this.markerTitle,
30206             draggable: this.draggable
30207         });
30208         
30209         return {
30210             map: _map,
30211             marker: _marker,
30212             circle: null,
30213             location: position,
30214             radius: this.radius,
30215             locationName: this.locationName,
30216             addressComponents: {
30217                 formatted_address: null,
30218                 addressLine1: null,
30219                 addressLine2: null,
30220                 streetName: null,
30221                 streetNumber: null,
30222                 city: null,
30223                 district: null,
30224                 state: null,
30225                 stateOrProvince: null
30226             },
30227             settings: this,
30228             domContainer: this.el.dom,
30229             geodecoder: new google.maps.Geocoder()
30230         };
30231     },
30232     
30233     drawCircle: function(center, radius, options) 
30234     {
30235         if (this.gMapContext.circle != null) {
30236             this.gMapContext.circle.setMap(null);
30237         }
30238         if (radius > 0) {
30239             radius *= 1;
30240             options = Roo.apply({}, options, {
30241                 strokeColor: "#0000FF",
30242                 strokeOpacity: .35,
30243                 strokeWeight: 2,
30244                 fillColor: "#0000FF",
30245                 fillOpacity: .2
30246             });
30247             
30248             options.map = this.gMapContext.map;
30249             options.radius = radius;
30250             options.center = center;
30251             this.gMapContext.circle = new google.maps.Circle(options);
30252             return this.gMapContext.circle;
30253         }
30254         
30255         return null;
30256     },
30257     
30258     setPosition: function(location) 
30259     {
30260         this.gMapContext.location = location;
30261         this.gMapContext.marker.setPosition(location);
30262         this.gMapContext.map.panTo(location);
30263         this.drawCircle(location, this.gMapContext.radius, {});
30264         
30265         var _this = this;
30266         
30267         if (this.gMapContext.settings.enableReverseGeocode) {
30268             this.gMapContext.geodecoder.geocode({
30269                 latLng: this.gMapContext.location
30270             }, function(results, status) {
30271                 
30272                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30273                     _this.gMapContext.locationName = results[0].formatted_address;
30274                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30275                     
30276                     _this.fireEvent('positionchanged', this, location);
30277                 }
30278             });
30279             
30280             return;
30281         }
30282         
30283         this.fireEvent('positionchanged', this, location);
30284     },
30285     
30286     resize: function()
30287     {
30288         google.maps.event.trigger(this.gMapContext.map, "resize");
30289         
30290         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30291         
30292         this.fireEvent('resize', this);
30293     },
30294     
30295     setPositionByLatLng: function(latitude, longitude)
30296     {
30297         this.setPosition(new google.maps.LatLng(latitude, longitude));
30298     },
30299     
30300     getCurrentPosition: function() 
30301     {
30302         return {
30303             latitude: this.gMapContext.location.lat(),
30304             longitude: this.gMapContext.location.lng()
30305         };
30306     },
30307     
30308     getAddressName: function() 
30309     {
30310         return this.gMapContext.locationName;
30311     },
30312     
30313     getAddressComponents: function() 
30314     {
30315         return this.gMapContext.addressComponents;
30316     },
30317     
30318     address_component_from_google_geocode: function(address_components) 
30319     {
30320         var result = {};
30321         
30322         for (var i = 0; i < address_components.length; i++) {
30323             var component = address_components[i];
30324             if (component.types.indexOf("postal_code") >= 0) {
30325                 result.postalCode = component.short_name;
30326             } else if (component.types.indexOf("street_number") >= 0) {
30327                 result.streetNumber = component.short_name;
30328             } else if (component.types.indexOf("route") >= 0) {
30329                 result.streetName = component.short_name;
30330             } else if (component.types.indexOf("neighborhood") >= 0) {
30331                 result.city = component.short_name;
30332             } else if (component.types.indexOf("locality") >= 0) {
30333                 result.city = component.short_name;
30334             } else if (component.types.indexOf("sublocality") >= 0) {
30335                 result.district = component.short_name;
30336             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30337                 result.stateOrProvince = component.short_name;
30338             } else if (component.types.indexOf("country") >= 0) {
30339                 result.country = component.short_name;
30340             }
30341         }
30342         
30343         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30344         result.addressLine2 = "";
30345         return result;
30346     },
30347     
30348     setZoomLevel: function(zoom)
30349     {
30350         this.gMapContext.map.setZoom(zoom);
30351     },
30352     
30353     show: function()
30354     {
30355         if(!this.el){
30356             return;
30357         }
30358         
30359         this.el.show();
30360         
30361         this.resize();
30362         
30363         this.fireEvent('show', this);
30364     },
30365     
30366     hide: function()
30367     {
30368         if(!this.el){
30369             return;
30370         }
30371         
30372         this.el.hide();
30373         
30374         this.fireEvent('hide', this);
30375     }
30376     
30377 });
30378
30379 Roo.apply(Roo.bootstrap.LocationPicker, {
30380     
30381     OverlayView : function(map, options)
30382     {
30383         options = options || {};
30384         
30385         this.setMap(map);
30386     }
30387     
30388     
30389 });/**
30390  * @class Roo.bootstrap.Alert
30391  * @extends Roo.bootstrap.Component
30392  * Bootstrap Alert class - shows an alert area box
30393  * eg
30394  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30395   Enter a valid email address
30396 </div>
30397  * @licence LGPL
30398  * @cfg {String} title The title of alert
30399  * @cfg {String} html The content of alert
30400  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30401  * @cfg {String} fa font-awesomeicon
30402  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30403  * @cfg {Boolean} close true to show a x closer
30404  * 
30405  * 
30406  * @constructor
30407  * Create a new alert
30408  * @param {Object} config The config object
30409  */
30410
30411
30412 Roo.bootstrap.Alert = function(config){
30413     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30414     
30415 };
30416
30417 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30418     
30419     title: '',
30420     html: '',
30421     weight: false,
30422     fa: false,
30423     faicon: false, // BC
30424     close : false,
30425     
30426     
30427     getAutoCreate : function()
30428     {
30429         
30430         var cfg = {
30431             tag : 'div',
30432             cls : 'alert',
30433             cn : [
30434                 {
30435                     tag: 'button',
30436                     type :  "button",
30437                     cls: "close",
30438                     html : '×',
30439                     style : this.close ? '' : 'display:none'
30440                 },
30441                 {
30442                     tag : 'i',
30443                     cls : 'roo-alert-icon'
30444                     
30445                 },
30446                 {
30447                     tag : 'b',
30448                     cls : 'roo-alert-title',
30449                     html : this.title
30450                 },
30451                 {
30452                     tag : 'span',
30453                     cls : 'roo-alert-text',
30454                     html : this.html
30455                 }
30456             ]
30457         };
30458         
30459         if(this.faicon){
30460             cfg.cn[0].cls += ' fa ' + this.faicon;
30461         }
30462         if(this.fa){
30463             cfg.cn[0].cls += ' fa ' + this.fa;
30464         }
30465         
30466         if(this.weight){
30467             cfg.cls += ' alert-' + this.weight;
30468         }
30469         
30470         return cfg;
30471     },
30472     
30473     initEvents: function() 
30474     {
30475         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30476         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30477         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30478         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30479         if (this.seconds > 0) {
30480             this.hide.defer(this.seconds, this);
30481         }
30482     },
30483     /**
30484      * Set the Title Message HTML
30485      * @param {String} html
30486      */
30487     setTitle : function(str)
30488     {
30489         this.titleEl.dom.innerHTML = str;
30490     },
30491      
30492      /**
30493      * Set the Body Message HTML
30494      * @param {String} html
30495      */
30496     setHtml : function(str)
30497     {
30498         this.htmlEl.dom.innerHTML = str;
30499     },
30500     /**
30501      * Set the Weight of the alert
30502      * @param {String} (success|info|warning|danger) weight
30503      */
30504     
30505     setWeight : function(weight)
30506     {
30507         if(this.weight){
30508             this.el.removeClass('alert-' + this.weight);
30509         }
30510         
30511         this.weight = weight;
30512         
30513         this.el.addClass('alert-' + this.weight);
30514     },
30515       /**
30516      * Set the Icon of the alert
30517      * @param {String} see fontawsome names (name without the 'fa-' bit)
30518      */
30519     setIcon : function(icon)
30520     {
30521         if(this.faicon){
30522             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30523         }
30524         
30525         this.faicon = icon;
30526         
30527         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30528     },
30529     /**
30530      * Hide the Alert
30531      */
30532     hide: function() 
30533     {
30534         this.el.hide();   
30535     },
30536     /**
30537      * Show the Alert
30538      */
30539     show: function() 
30540     {  
30541         this.el.show();   
30542     }
30543     
30544 });
30545
30546  
30547 /*
30548 * Licence: LGPL
30549 */
30550
30551 /**
30552  * @class Roo.bootstrap.UploadCropbox
30553  * @extends Roo.bootstrap.Component
30554  * Bootstrap UploadCropbox class
30555  * @cfg {String} emptyText show when image has been loaded
30556  * @cfg {String} rotateNotify show when image too small to rotate
30557  * @cfg {Number} errorTimeout default 3000
30558  * @cfg {Number} minWidth default 300
30559  * @cfg {Number} minHeight default 300
30560  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30561  * @cfg {Boolean} isDocument (true|false) default false
30562  * @cfg {String} url action url
30563  * @cfg {String} paramName default 'imageUpload'
30564  * @cfg {String} method default POST
30565  * @cfg {Boolean} loadMask (true|false) default true
30566  * @cfg {Boolean} loadingText default 'Loading...'
30567  * 
30568  * @constructor
30569  * Create a new UploadCropbox
30570  * @param {Object} config The config object
30571  */
30572
30573 Roo.bootstrap.UploadCropbox = function(config){
30574     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30575     
30576     this.addEvents({
30577         /**
30578          * @event beforeselectfile
30579          * Fire before select file
30580          * @param {Roo.bootstrap.UploadCropbox} this
30581          */
30582         "beforeselectfile" : true,
30583         /**
30584          * @event initial
30585          * Fire after initEvent
30586          * @param {Roo.bootstrap.UploadCropbox} this
30587          */
30588         "initial" : true,
30589         /**
30590          * @event crop
30591          * Fire after initEvent
30592          * @param {Roo.bootstrap.UploadCropbox} this
30593          * @param {String} data
30594          */
30595         "crop" : true,
30596         /**
30597          * @event prepare
30598          * Fire when preparing the file data
30599          * @param {Roo.bootstrap.UploadCropbox} this
30600          * @param {Object} file
30601          */
30602         "prepare" : true,
30603         /**
30604          * @event exception
30605          * Fire when get exception
30606          * @param {Roo.bootstrap.UploadCropbox} this
30607          * @param {XMLHttpRequest} xhr
30608          */
30609         "exception" : true,
30610         /**
30611          * @event beforeloadcanvas
30612          * Fire before load the canvas
30613          * @param {Roo.bootstrap.UploadCropbox} this
30614          * @param {String} src
30615          */
30616         "beforeloadcanvas" : true,
30617         /**
30618          * @event trash
30619          * Fire when trash image
30620          * @param {Roo.bootstrap.UploadCropbox} this
30621          */
30622         "trash" : true,
30623         /**
30624          * @event download
30625          * Fire when download the image
30626          * @param {Roo.bootstrap.UploadCropbox} this
30627          */
30628         "download" : true,
30629         /**
30630          * @event footerbuttonclick
30631          * Fire when footerbuttonclick
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          * @param {String} type
30634          */
30635         "footerbuttonclick" : true,
30636         /**
30637          * @event resize
30638          * Fire when resize
30639          * @param {Roo.bootstrap.UploadCropbox} this
30640          */
30641         "resize" : true,
30642         /**
30643          * @event rotate
30644          * Fire when rotate the image
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          * @param {String} pos
30647          */
30648         "rotate" : true,
30649         /**
30650          * @event inspect
30651          * Fire when inspect the file
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          * @param {Object} file
30654          */
30655         "inspect" : true,
30656         /**
30657          * @event upload
30658          * Fire when xhr upload the file
30659          * @param {Roo.bootstrap.UploadCropbox} this
30660          * @param {Object} data
30661          */
30662         "upload" : true,
30663         /**
30664          * @event arrange
30665          * Fire when arrange the file data
30666          * @param {Roo.bootstrap.UploadCropbox} this
30667          * @param {Object} formData
30668          */
30669         "arrange" : true
30670     });
30671     
30672     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30673 };
30674
30675 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30676     
30677     emptyText : 'Click to upload image',
30678     rotateNotify : 'Image is too small to rotate',
30679     errorTimeout : 3000,
30680     scale : 0,
30681     baseScale : 1,
30682     rotate : 0,
30683     dragable : false,
30684     pinching : false,
30685     mouseX : 0,
30686     mouseY : 0,
30687     cropData : false,
30688     minWidth : 300,
30689     minHeight : 300,
30690     file : false,
30691     exif : {},
30692     baseRotate : 1,
30693     cropType : 'image/jpeg',
30694     buttons : false,
30695     canvasLoaded : false,
30696     isDocument : false,
30697     method : 'POST',
30698     paramName : 'imageUpload',
30699     loadMask : true,
30700     loadingText : 'Loading...',
30701     maskEl : false,
30702     
30703     getAutoCreate : function()
30704     {
30705         var cfg = {
30706             tag : 'div',
30707             cls : 'roo-upload-cropbox',
30708             cn : [
30709                 {
30710                     tag : 'input',
30711                     cls : 'roo-upload-cropbox-selector',
30712                     type : 'file'
30713                 },
30714                 {
30715                     tag : 'div',
30716                     cls : 'roo-upload-cropbox-body',
30717                     style : 'cursor:pointer',
30718                     cn : [
30719                         {
30720                             tag : 'div',
30721                             cls : 'roo-upload-cropbox-preview'
30722                         },
30723                         {
30724                             tag : 'div',
30725                             cls : 'roo-upload-cropbox-thumb'
30726                         },
30727                         {
30728                             tag : 'div',
30729                             cls : 'roo-upload-cropbox-empty-notify',
30730                             html : this.emptyText
30731                         },
30732                         {
30733                             tag : 'div',
30734                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30735                             html : this.rotateNotify
30736                         }
30737                     ]
30738                 },
30739                 {
30740                     tag : 'div',
30741                     cls : 'roo-upload-cropbox-footer',
30742                     cn : {
30743                         tag : 'div',
30744                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30745                         cn : []
30746                     }
30747                 }
30748             ]
30749         };
30750         
30751         return cfg;
30752     },
30753     
30754     onRender : function(ct, position)
30755     {
30756         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30757         
30758         if (this.buttons.length) {
30759             
30760             Roo.each(this.buttons, function(bb) {
30761                 
30762                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30763                 
30764                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30765                 
30766             }, this);
30767         }
30768         
30769         if(this.loadMask){
30770             this.maskEl = this.el;
30771         }
30772     },
30773     
30774     initEvents : function()
30775     {
30776         this.urlAPI = (window.createObjectURL && window) || 
30777                                 (window.URL && URL.revokeObjectURL && URL) || 
30778                                 (window.webkitURL && webkitURL);
30779                         
30780         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30781         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30782         
30783         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30784         this.selectorEl.hide();
30785         
30786         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30787         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30788         
30789         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30790         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30791         this.thumbEl.hide();
30792         
30793         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30794         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795         
30796         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30797         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30798         this.errorEl.hide();
30799         
30800         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30801         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802         this.footerEl.hide();
30803         
30804         this.setThumbBoxSize();
30805         
30806         this.bind();
30807         
30808         this.resize();
30809         
30810         this.fireEvent('initial', this);
30811     },
30812
30813     bind : function()
30814     {
30815         var _this = this;
30816         
30817         window.addEventListener("resize", function() { _this.resize(); } );
30818         
30819         this.bodyEl.on('click', this.beforeSelectFile, this);
30820         
30821         if(Roo.isTouch){
30822             this.bodyEl.on('touchstart', this.onTouchStart, this);
30823             this.bodyEl.on('touchmove', this.onTouchMove, this);
30824             this.bodyEl.on('touchend', this.onTouchEnd, this);
30825         }
30826         
30827         if(!Roo.isTouch){
30828             this.bodyEl.on('mousedown', this.onMouseDown, this);
30829             this.bodyEl.on('mousemove', this.onMouseMove, this);
30830             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30831             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30832             Roo.get(document).on('mouseup', this.onMouseUp, this);
30833         }
30834         
30835         this.selectorEl.on('change', this.onFileSelected, this);
30836     },
30837     
30838     reset : function()
30839     {    
30840         this.scale = 0;
30841         this.baseScale = 1;
30842         this.rotate = 0;
30843         this.baseRotate = 1;
30844         this.dragable = false;
30845         this.pinching = false;
30846         this.mouseX = 0;
30847         this.mouseY = 0;
30848         this.cropData = false;
30849         this.notifyEl.dom.innerHTML = this.emptyText;
30850         
30851         this.selectorEl.dom.value = '';
30852         
30853     },
30854     
30855     resize : function()
30856     {
30857         if(this.fireEvent('resize', this) != false){
30858             this.setThumbBoxPosition();
30859             this.setCanvasPosition();
30860         }
30861     },
30862     
30863     onFooterButtonClick : function(e, el, o, type)
30864     {
30865         switch (type) {
30866             case 'rotate-left' :
30867                 this.onRotateLeft(e);
30868                 break;
30869             case 'rotate-right' :
30870                 this.onRotateRight(e);
30871                 break;
30872             case 'picture' :
30873                 this.beforeSelectFile(e);
30874                 break;
30875             case 'trash' :
30876                 this.trash(e);
30877                 break;
30878             case 'crop' :
30879                 this.crop(e);
30880                 break;
30881             case 'download' :
30882                 this.download(e);
30883                 break;
30884             default :
30885                 break;
30886         }
30887         
30888         this.fireEvent('footerbuttonclick', this, type);
30889     },
30890     
30891     beforeSelectFile : function(e)
30892     {
30893         e.preventDefault();
30894         
30895         if(this.fireEvent('beforeselectfile', this) != false){
30896             this.selectorEl.dom.click();
30897         }
30898     },
30899     
30900     onFileSelected : function(e)
30901     {
30902         e.preventDefault();
30903         
30904         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30905             return;
30906         }
30907         
30908         var file = this.selectorEl.dom.files[0];
30909         
30910         if(this.fireEvent('inspect', this, file) != false){
30911             this.prepare(file);
30912         }
30913         
30914     },
30915     
30916     trash : function(e)
30917     {
30918         this.fireEvent('trash', this);
30919     },
30920     
30921     download : function(e)
30922     {
30923         this.fireEvent('download', this);
30924     },
30925     
30926     loadCanvas : function(src)
30927     {   
30928         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30929             
30930             this.reset();
30931             
30932             this.imageEl = document.createElement('img');
30933             
30934             var _this = this;
30935             
30936             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30937             
30938             this.imageEl.src = src;
30939         }
30940     },
30941     
30942     onLoadCanvas : function()
30943     {   
30944         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30945         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30946         
30947         this.bodyEl.un('click', this.beforeSelectFile, this);
30948         
30949         this.notifyEl.hide();
30950         this.thumbEl.show();
30951         this.footerEl.show();
30952         
30953         this.baseRotateLevel();
30954         
30955         if(this.isDocument){
30956             this.setThumbBoxSize();
30957         }
30958         
30959         this.setThumbBoxPosition();
30960         
30961         this.baseScaleLevel();
30962         
30963         this.draw();
30964         
30965         this.resize();
30966         
30967         this.canvasLoaded = true;
30968         
30969         if(this.loadMask){
30970             this.maskEl.unmask();
30971         }
30972         
30973     },
30974     
30975     setCanvasPosition : function()
30976     {   
30977         if(!this.canvasEl){
30978             return;
30979         }
30980         
30981         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30982         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30983         
30984         this.previewEl.setLeft(pw);
30985         this.previewEl.setTop(ph);
30986         
30987     },
30988     
30989     onMouseDown : function(e)
30990     {   
30991         e.stopEvent();
30992         
30993         this.dragable = true;
30994         this.pinching = false;
30995         
30996         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30997             this.dragable = false;
30998             return;
30999         }
31000         
31001         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31002         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31003         
31004     },
31005     
31006     onMouseMove : function(e)
31007     {   
31008         e.stopEvent();
31009         
31010         if(!this.canvasLoaded){
31011             return;
31012         }
31013         
31014         if (!this.dragable){
31015             return;
31016         }
31017         
31018         var minX = Math.ceil(this.thumbEl.getLeft(true));
31019         var minY = Math.ceil(this.thumbEl.getTop(true));
31020         
31021         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31022         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31023         
31024         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31025         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31026         
31027         x = x - this.mouseX;
31028         y = y - this.mouseY;
31029         
31030         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31031         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31032         
31033         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31034         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31035         
31036         this.previewEl.setLeft(bgX);
31037         this.previewEl.setTop(bgY);
31038         
31039         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31040         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31041     },
31042     
31043     onMouseUp : function(e)
31044     {   
31045         e.stopEvent();
31046         
31047         this.dragable = false;
31048     },
31049     
31050     onMouseWheel : function(e)
31051     {   
31052         e.stopEvent();
31053         
31054         this.startScale = this.scale;
31055         
31056         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31057         
31058         if(!this.zoomable()){
31059             this.scale = this.startScale;
31060             return;
31061         }
31062         
31063         this.draw();
31064         
31065         return;
31066     },
31067     
31068     zoomable : function()
31069     {
31070         var minScale = this.thumbEl.getWidth() / this.minWidth;
31071         
31072         if(this.minWidth < this.minHeight){
31073             minScale = this.thumbEl.getHeight() / this.minHeight;
31074         }
31075         
31076         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31077         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31078         
31079         if(
31080                 this.isDocument &&
31081                 (this.rotate == 0 || this.rotate == 180) && 
31082                 (
31083                     width > this.imageEl.OriginWidth || 
31084                     height > this.imageEl.OriginHeight ||
31085                     (width < this.minWidth && height < this.minHeight)
31086                 )
31087         ){
31088             return false;
31089         }
31090         
31091         if(
31092                 this.isDocument &&
31093                 (this.rotate == 90 || this.rotate == 270) && 
31094                 (
31095                     width > this.imageEl.OriginWidth || 
31096                     height > this.imageEl.OriginHeight ||
31097                     (width < this.minHeight && height < this.minWidth)
31098                 )
31099         ){
31100             return false;
31101         }
31102         
31103         if(
31104                 !this.isDocument &&
31105                 (this.rotate == 0 || this.rotate == 180) && 
31106                 (
31107                     width < this.minWidth || 
31108                     width > this.imageEl.OriginWidth || 
31109                     height < this.minHeight || 
31110                     height > this.imageEl.OriginHeight
31111                 )
31112         ){
31113             return false;
31114         }
31115         
31116         if(
31117                 !this.isDocument &&
31118                 (this.rotate == 90 || this.rotate == 270) && 
31119                 (
31120                     width < this.minHeight || 
31121                     width > this.imageEl.OriginWidth || 
31122                     height < this.minWidth || 
31123                     height > this.imageEl.OriginHeight
31124                 )
31125         ){
31126             return false;
31127         }
31128         
31129         return true;
31130         
31131     },
31132     
31133     onRotateLeft : function(e)
31134     {   
31135         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31136             
31137             var minScale = this.thumbEl.getWidth() / this.minWidth;
31138             
31139             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31140             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31141             
31142             this.startScale = this.scale;
31143             
31144             while (this.getScaleLevel() < minScale){
31145             
31146                 this.scale = this.scale + 1;
31147                 
31148                 if(!this.zoomable()){
31149                     break;
31150                 }
31151                 
31152                 if(
31153                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31154                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31155                 ){
31156                     continue;
31157                 }
31158                 
31159                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31160
31161                 this.draw();
31162                 
31163                 return;
31164             }
31165             
31166             this.scale = this.startScale;
31167             
31168             this.onRotateFail();
31169             
31170             return false;
31171         }
31172         
31173         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31174
31175         if(this.isDocument){
31176             this.setThumbBoxSize();
31177             this.setThumbBoxPosition();
31178             this.setCanvasPosition();
31179         }
31180         
31181         this.draw();
31182         
31183         this.fireEvent('rotate', this, 'left');
31184         
31185     },
31186     
31187     onRotateRight : function(e)
31188     {
31189         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31190             
31191             var minScale = this.thumbEl.getWidth() / this.minWidth;
31192         
31193             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31194             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31195             
31196             this.startScale = this.scale;
31197             
31198             while (this.getScaleLevel() < minScale){
31199             
31200                 this.scale = this.scale + 1;
31201                 
31202                 if(!this.zoomable()){
31203                     break;
31204                 }
31205                 
31206                 if(
31207                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31208                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31209                 ){
31210                     continue;
31211                 }
31212                 
31213                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31214
31215                 this.draw();
31216                 
31217                 return;
31218             }
31219             
31220             this.scale = this.startScale;
31221             
31222             this.onRotateFail();
31223             
31224             return false;
31225         }
31226         
31227         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31228
31229         if(this.isDocument){
31230             this.setThumbBoxSize();
31231             this.setThumbBoxPosition();
31232             this.setCanvasPosition();
31233         }
31234         
31235         this.draw();
31236         
31237         this.fireEvent('rotate', this, 'right');
31238     },
31239     
31240     onRotateFail : function()
31241     {
31242         this.errorEl.show(true);
31243         
31244         var _this = this;
31245         
31246         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31247     },
31248     
31249     draw : function()
31250     {
31251         this.previewEl.dom.innerHTML = '';
31252         
31253         var canvasEl = document.createElement("canvas");
31254         
31255         var contextEl = canvasEl.getContext("2d");
31256         
31257         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31258         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31259         var center = this.imageEl.OriginWidth / 2;
31260         
31261         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31262             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31263             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31264             center = this.imageEl.OriginHeight / 2;
31265         }
31266         
31267         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31268         
31269         contextEl.translate(center, center);
31270         contextEl.rotate(this.rotate * Math.PI / 180);
31271
31272         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31273         
31274         this.canvasEl = document.createElement("canvas");
31275         
31276         this.contextEl = this.canvasEl.getContext("2d");
31277         
31278         switch (this.rotate) {
31279             case 0 :
31280                 
31281                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31282                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31283                 
31284                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31285                 
31286                 break;
31287             case 90 : 
31288                 
31289                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31290                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31291                 
31292                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31293                     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);
31294                     break;
31295                 }
31296                 
31297                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31298                 
31299                 break;
31300             case 180 :
31301                 
31302                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31303                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31304                 
31305                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31306                     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);
31307                     break;
31308                 }
31309                 
31310                 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);
31311                 
31312                 break;
31313             case 270 :
31314                 
31315                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31316                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31317         
31318                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31319                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31320                     break;
31321                 }
31322                 
31323                 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);
31324                 
31325                 break;
31326             default : 
31327                 break;
31328         }
31329         
31330         this.previewEl.appendChild(this.canvasEl);
31331         
31332         this.setCanvasPosition();
31333     },
31334     
31335     crop : function()
31336     {
31337         if(!this.canvasLoaded){
31338             return;
31339         }
31340         
31341         var imageCanvas = document.createElement("canvas");
31342         
31343         var imageContext = imageCanvas.getContext("2d");
31344         
31345         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31346         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31347         
31348         var center = imageCanvas.width / 2;
31349         
31350         imageContext.translate(center, center);
31351         
31352         imageContext.rotate(this.rotate * Math.PI / 180);
31353         
31354         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31355         
31356         var canvas = document.createElement("canvas");
31357         
31358         var context = canvas.getContext("2d");
31359                 
31360         canvas.width = this.minWidth;
31361         canvas.height = this.minHeight;
31362
31363         switch (this.rotate) {
31364             case 0 :
31365                 
31366                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31367                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31368                 
31369                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31370                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31371                 
31372                 var targetWidth = this.minWidth - 2 * x;
31373                 var targetHeight = this.minHeight - 2 * y;
31374                 
31375                 var scale = 1;
31376                 
31377                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31378                     scale = targetWidth / width;
31379                 }
31380                 
31381                 if(x > 0 && y == 0){
31382                     scale = targetHeight / height;
31383                 }
31384                 
31385                 if(x > 0 && y > 0){
31386                     scale = targetWidth / width;
31387                     
31388                     if(width < height){
31389                         scale = targetHeight / height;
31390                     }
31391                 }
31392                 
31393                 context.scale(scale, scale);
31394                 
31395                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31396                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31397
31398                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31399                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31400
31401                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31402                 
31403                 break;
31404             case 90 : 
31405                 
31406                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31407                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31408                 
31409                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31410                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31411                 
31412                 var targetWidth = this.minWidth - 2 * x;
31413                 var targetHeight = this.minHeight - 2 * y;
31414                 
31415                 var scale = 1;
31416                 
31417                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31418                     scale = targetWidth / width;
31419                 }
31420                 
31421                 if(x > 0 && y == 0){
31422                     scale = targetHeight / height;
31423                 }
31424                 
31425                 if(x > 0 && y > 0){
31426                     scale = targetWidth / width;
31427                     
31428                     if(width < height){
31429                         scale = targetHeight / height;
31430                     }
31431                 }
31432                 
31433                 context.scale(scale, scale);
31434                 
31435                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31436                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31437
31438                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31439                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31440                 
31441                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31442                 
31443                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31444                 
31445                 break;
31446             case 180 :
31447                 
31448                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31449                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31450                 
31451                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31452                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31453                 
31454                 var targetWidth = this.minWidth - 2 * x;
31455                 var targetHeight = this.minHeight - 2 * y;
31456                 
31457                 var scale = 1;
31458                 
31459                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31460                     scale = targetWidth / width;
31461                 }
31462                 
31463                 if(x > 0 && y == 0){
31464                     scale = targetHeight / height;
31465                 }
31466                 
31467                 if(x > 0 && y > 0){
31468                     scale = targetWidth / width;
31469                     
31470                     if(width < height){
31471                         scale = targetHeight / height;
31472                     }
31473                 }
31474                 
31475                 context.scale(scale, scale);
31476                 
31477                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31478                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31479
31480                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31481                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31482
31483                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31484                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31485                 
31486                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31487                 
31488                 break;
31489             case 270 :
31490                 
31491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31493                 
31494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31496                 
31497                 var targetWidth = this.minWidth - 2 * x;
31498                 var targetHeight = this.minHeight - 2 * y;
31499                 
31500                 var scale = 1;
31501                 
31502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31503                     scale = targetWidth / width;
31504                 }
31505                 
31506                 if(x > 0 && y == 0){
31507                     scale = targetHeight / height;
31508                 }
31509                 
31510                 if(x > 0 && y > 0){
31511                     scale = targetWidth / width;
31512                     
31513                     if(width < height){
31514                         scale = targetHeight / height;
31515                     }
31516                 }
31517                 
31518                 context.scale(scale, scale);
31519                 
31520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31522
31523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31525                 
31526                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31527                 
31528                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31529                 
31530                 break;
31531             default : 
31532                 break;
31533         }
31534         
31535         this.cropData = canvas.toDataURL(this.cropType);
31536         
31537         if(this.fireEvent('crop', this, this.cropData) !== false){
31538             this.process(this.file, this.cropData);
31539         }
31540         
31541         return;
31542         
31543     },
31544     
31545     setThumbBoxSize : function()
31546     {
31547         var width, height;
31548         
31549         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31550             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31551             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31552             
31553             this.minWidth = width;
31554             this.minHeight = height;
31555             
31556             if(this.rotate == 90 || this.rotate == 270){
31557                 this.minWidth = height;
31558                 this.minHeight = width;
31559             }
31560         }
31561         
31562         height = 300;
31563         width = Math.ceil(this.minWidth * height / this.minHeight);
31564         
31565         if(this.minWidth > this.minHeight){
31566             width = 300;
31567             height = Math.ceil(this.minHeight * width / this.minWidth);
31568         }
31569         
31570         this.thumbEl.setStyle({
31571             width : width + 'px',
31572             height : height + 'px'
31573         });
31574
31575         return;
31576             
31577     },
31578     
31579     setThumbBoxPosition : function()
31580     {
31581         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31582         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31583         
31584         this.thumbEl.setLeft(x);
31585         this.thumbEl.setTop(y);
31586         
31587     },
31588     
31589     baseRotateLevel : function()
31590     {
31591         this.baseRotate = 1;
31592         
31593         if(
31594                 typeof(this.exif) != 'undefined' &&
31595                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31596                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31597         ){
31598             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31599         }
31600         
31601         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31602         
31603     },
31604     
31605     baseScaleLevel : function()
31606     {
31607         var width, height;
31608         
31609         if(this.isDocument){
31610             
31611             if(this.baseRotate == 6 || this.baseRotate == 8){
31612             
31613                 height = this.thumbEl.getHeight();
31614                 this.baseScale = height / this.imageEl.OriginWidth;
31615
31616                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31617                     width = this.thumbEl.getWidth();
31618                     this.baseScale = width / this.imageEl.OriginHeight;
31619                 }
31620
31621                 return;
31622             }
31623
31624             height = this.thumbEl.getHeight();
31625             this.baseScale = height / this.imageEl.OriginHeight;
31626
31627             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31628                 width = this.thumbEl.getWidth();
31629                 this.baseScale = width / this.imageEl.OriginWidth;
31630             }
31631
31632             return;
31633         }
31634         
31635         if(this.baseRotate == 6 || this.baseRotate == 8){
31636             
31637             width = this.thumbEl.getHeight();
31638             this.baseScale = width / this.imageEl.OriginHeight;
31639             
31640             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31641                 height = this.thumbEl.getWidth();
31642                 this.baseScale = height / this.imageEl.OriginHeight;
31643             }
31644             
31645             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31646                 height = this.thumbEl.getWidth();
31647                 this.baseScale = height / this.imageEl.OriginHeight;
31648                 
31649                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31650                     width = this.thumbEl.getHeight();
31651                     this.baseScale = width / this.imageEl.OriginWidth;
31652                 }
31653             }
31654             
31655             return;
31656         }
31657         
31658         width = this.thumbEl.getWidth();
31659         this.baseScale = width / this.imageEl.OriginWidth;
31660         
31661         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31662             height = this.thumbEl.getHeight();
31663             this.baseScale = height / this.imageEl.OriginHeight;
31664         }
31665         
31666         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31667             
31668             height = this.thumbEl.getHeight();
31669             this.baseScale = height / this.imageEl.OriginHeight;
31670             
31671             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31672                 width = this.thumbEl.getWidth();
31673                 this.baseScale = width / this.imageEl.OriginWidth;
31674             }
31675             
31676         }
31677         
31678         return;
31679     },
31680     
31681     getScaleLevel : function()
31682     {
31683         return this.baseScale * Math.pow(1.1, this.scale);
31684     },
31685     
31686     onTouchStart : function(e)
31687     {
31688         if(!this.canvasLoaded){
31689             this.beforeSelectFile(e);
31690             return;
31691         }
31692         
31693         var touches = e.browserEvent.touches;
31694         
31695         if(!touches){
31696             return;
31697         }
31698         
31699         if(touches.length == 1){
31700             this.onMouseDown(e);
31701             return;
31702         }
31703         
31704         if(touches.length != 2){
31705             return;
31706         }
31707         
31708         var coords = [];
31709         
31710         for(var i = 0, finger; finger = touches[i]; i++){
31711             coords.push(finger.pageX, finger.pageY);
31712         }
31713         
31714         var x = Math.pow(coords[0] - coords[2], 2);
31715         var y = Math.pow(coords[1] - coords[3], 2);
31716         
31717         this.startDistance = Math.sqrt(x + y);
31718         
31719         this.startScale = this.scale;
31720         
31721         this.pinching = true;
31722         this.dragable = false;
31723         
31724     },
31725     
31726     onTouchMove : function(e)
31727     {
31728         if(!this.pinching && !this.dragable){
31729             return;
31730         }
31731         
31732         var touches = e.browserEvent.touches;
31733         
31734         if(!touches){
31735             return;
31736         }
31737         
31738         if(this.dragable){
31739             this.onMouseMove(e);
31740             return;
31741         }
31742         
31743         var coords = [];
31744         
31745         for(var i = 0, finger; finger = touches[i]; i++){
31746             coords.push(finger.pageX, finger.pageY);
31747         }
31748         
31749         var x = Math.pow(coords[0] - coords[2], 2);
31750         var y = Math.pow(coords[1] - coords[3], 2);
31751         
31752         this.endDistance = Math.sqrt(x + y);
31753         
31754         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31755         
31756         if(!this.zoomable()){
31757             this.scale = this.startScale;
31758             return;
31759         }
31760         
31761         this.draw();
31762         
31763     },
31764     
31765     onTouchEnd : function(e)
31766     {
31767         this.pinching = false;
31768         this.dragable = false;
31769         
31770     },
31771     
31772     process : function(file, crop)
31773     {
31774         if(this.loadMask){
31775             this.maskEl.mask(this.loadingText);
31776         }
31777         
31778         this.xhr = new XMLHttpRequest();
31779         
31780         file.xhr = this.xhr;
31781
31782         this.xhr.open(this.method, this.url, true);
31783         
31784         var headers = {
31785             "Accept": "application/json",
31786             "Cache-Control": "no-cache",
31787             "X-Requested-With": "XMLHttpRequest"
31788         };
31789         
31790         for (var headerName in headers) {
31791             var headerValue = headers[headerName];
31792             if (headerValue) {
31793                 this.xhr.setRequestHeader(headerName, headerValue);
31794             }
31795         }
31796         
31797         var _this = this;
31798         
31799         this.xhr.onload = function()
31800         {
31801             _this.xhrOnLoad(_this.xhr);
31802         }
31803         
31804         this.xhr.onerror = function()
31805         {
31806             _this.xhrOnError(_this.xhr);
31807         }
31808         
31809         var formData = new FormData();
31810
31811         formData.append('returnHTML', 'NO');
31812         
31813         if(crop){
31814             formData.append('crop', crop);
31815         }
31816         
31817         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31818             formData.append(this.paramName, file, file.name);
31819         }
31820         
31821         if(typeof(file.filename) != 'undefined'){
31822             formData.append('filename', file.filename);
31823         }
31824         
31825         if(typeof(file.mimetype) != 'undefined'){
31826             formData.append('mimetype', file.mimetype);
31827         }
31828         
31829         if(this.fireEvent('arrange', this, formData) != false){
31830             this.xhr.send(formData);
31831         };
31832     },
31833     
31834     xhrOnLoad : function(xhr)
31835     {
31836         if(this.loadMask){
31837             this.maskEl.unmask();
31838         }
31839         
31840         if (xhr.readyState !== 4) {
31841             this.fireEvent('exception', this, xhr);
31842             return;
31843         }
31844
31845         var response = Roo.decode(xhr.responseText);
31846         
31847         if(!response.success){
31848             this.fireEvent('exception', this, xhr);
31849             return;
31850         }
31851         
31852         var response = Roo.decode(xhr.responseText);
31853         
31854         this.fireEvent('upload', this, response);
31855         
31856     },
31857     
31858     xhrOnError : function()
31859     {
31860         if(this.loadMask){
31861             this.maskEl.unmask();
31862         }
31863         
31864         Roo.log('xhr on error');
31865         
31866         var response = Roo.decode(xhr.responseText);
31867           
31868         Roo.log(response);
31869         
31870     },
31871     
31872     prepare : function(file)
31873     {   
31874         if(this.loadMask){
31875             this.maskEl.mask(this.loadingText);
31876         }
31877         
31878         this.file = false;
31879         this.exif = {};
31880         
31881         if(typeof(file) === 'string'){
31882             this.loadCanvas(file);
31883             return;
31884         }
31885         
31886         if(!file || !this.urlAPI){
31887             return;
31888         }
31889         
31890         this.file = file;
31891         this.cropType = file.type;
31892         
31893         var _this = this;
31894         
31895         if(this.fireEvent('prepare', this, this.file) != false){
31896             
31897             var reader = new FileReader();
31898             
31899             reader.onload = function (e) {
31900                 if (e.target.error) {
31901                     Roo.log(e.target.error);
31902                     return;
31903                 }
31904                 
31905                 var buffer = e.target.result,
31906                     dataView = new DataView(buffer),
31907                     offset = 2,
31908                     maxOffset = dataView.byteLength - 4,
31909                     markerBytes,
31910                     markerLength;
31911                 
31912                 if (dataView.getUint16(0) === 0xffd8) {
31913                     while (offset < maxOffset) {
31914                         markerBytes = dataView.getUint16(offset);
31915                         
31916                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31917                             markerLength = dataView.getUint16(offset + 2) + 2;
31918                             if (offset + markerLength > dataView.byteLength) {
31919                                 Roo.log('Invalid meta data: Invalid segment size.');
31920                                 break;
31921                             }
31922                             
31923                             if(markerBytes == 0xffe1){
31924                                 _this.parseExifData(
31925                                     dataView,
31926                                     offset,
31927                                     markerLength
31928                                 );
31929                             }
31930                             
31931                             offset += markerLength;
31932                             
31933                             continue;
31934                         }
31935                         
31936                         break;
31937                     }
31938                     
31939                 }
31940                 
31941                 var url = _this.urlAPI.createObjectURL(_this.file);
31942                 
31943                 _this.loadCanvas(url);
31944                 
31945                 return;
31946             }
31947             
31948             reader.readAsArrayBuffer(this.file);
31949             
31950         }
31951         
31952     },
31953     
31954     parseExifData : function(dataView, offset, length)
31955     {
31956         var tiffOffset = offset + 10,
31957             littleEndian,
31958             dirOffset;
31959     
31960         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31961             // No Exif data, might be XMP data instead
31962             return;
31963         }
31964         
31965         // Check for the ASCII code for "Exif" (0x45786966):
31966         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31967             // No Exif data, might be XMP data instead
31968             return;
31969         }
31970         if (tiffOffset + 8 > dataView.byteLength) {
31971             Roo.log('Invalid Exif data: Invalid segment size.');
31972             return;
31973         }
31974         // Check for the two null bytes:
31975         if (dataView.getUint16(offset + 8) !== 0x0000) {
31976             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31977             return;
31978         }
31979         // Check the byte alignment:
31980         switch (dataView.getUint16(tiffOffset)) {
31981         case 0x4949:
31982             littleEndian = true;
31983             break;
31984         case 0x4D4D:
31985             littleEndian = false;
31986             break;
31987         default:
31988             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31989             return;
31990         }
31991         // Check for the TIFF tag marker (0x002A):
31992         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31993             Roo.log('Invalid Exif data: Missing TIFF marker.');
31994             return;
31995         }
31996         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31997         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31998         
31999         this.parseExifTags(
32000             dataView,
32001             tiffOffset,
32002             tiffOffset + dirOffset,
32003             littleEndian
32004         );
32005     },
32006     
32007     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32008     {
32009         var tagsNumber,
32010             dirEndOffset,
32011             i;
32012         if (dirOffset + 6 > dataView.byteLength) {
32013             Roo.log('Invalid Exif data: Invalid directory offset.');
32014             return;
32015         }
32016         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32017         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32018         if (dirEndOffset + 4 > dataView.byteLength) {
32019             Roo.log('Invalid Exif data: Invalid directory size.');
32020             return;
32021         }
32022         for (i = 0; i < tagsNumber; i += 1) {
32023             this.parseExifTag(
32024                 dataView,
32025                 tiffOffset,
32026                 dirOffset + 2 + 12 * i, // tag offset
32027                 littleEndian
32028             );
32029         }
32030         // Return the offset to the next directory:
32031         return dataView.getUint32(dirEndOffset, littleEndian);
32032     },
32033     
32034     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32035     {
32036         var tag = dataView.getUint16(offset, littleEndian);
32037         
32038         this.exif[tag] = this.getExifValue(
32039             dataView,
32040             tiffOffset,
32041             offset,
32042             dataView.getUint16(offset + 2, littleEndian), // tag type
32043             dataView.getUint32(offset + 4, littleEndian), // tag length
32044             littleEndian
32045         );
32046     },
32047     
32048     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32049     {
32050         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32051             tagSize,
32052             dataOffset,
32053             values,
32054             i,
32055             str,
32056             c;
32057     
32058         if (!tagType) {
32059             Roo.log('Invalid Exif data: Invalid tag type.');
32060             return;
32061         }
32062         
32063         tagSize = tagType.size * length;
32064         // Determine if the value is contained in the dataOffset bytes,
32065         // or if the value at the dataOffset is a pointer to the actual data:
32066         dataOffset = tagSize > 4 ?
32067                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32068         if (dataOffset + tagSize > dataView.byteLength) {
32069             Roo.log('Invalid Exif data: Invalid data offset.');
32070             return;
32071         }
32072         if (length === 1) {
32073             return tagType.getValue(dataView, dataOffset, littleEndian);
32074         }
32075         values = [];
32076         for (i = 0; i < length; i += 1) {
32077             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32078         }
32079         
32080         if (tagType.ascii) {
32081             str = '';
32082             // Concatenate the chars:
32083             for (i = 0; i < values.length; i += 1) {
32084                 c = values[i];
32085                 // Ignore the terminating NULL byte(s):
32086                 if (c === '\u0000') {
32087                     break;
32088                 }
32089                 str += c;
32090             }
32091             return str;
32092         }
32093         return values;
32094     }
32095     
32096 });
32097
32098 Roo.apply(Roo.bootstrap.UploadCropbox, {
32099     tags : {
32100         'Orientation': 0x0112
32101     },
32102     
32103     Orientation: {
32104             1: 0, //'top-left',
32105 //            2: 'top-right',
32106             3: 180, //'bottom-right',
32107 //            4: 'bottom-left',
32108 //            5: 'left-top',
32109             6: 90, //'right-top',
32110 //            7: 'right-bottom',
32111             8: 270 //'left-bottom'
32112     },
32113     
32114     exifTagTypes : {
32115         // byte, 8-bit unsigned int:
32116         1: {
32117             getValue: function (dataView, dataOffset) {
32118                 return dataView.getUint8(dataOffset);
32119             },
32120             size: 1
32121         },
32122         // ascii, 8-bit byte:
32123         2: {
32124             getValue: function (dataView, dataOffset) {
32125                 return String.fromCharCode(dataView.getUint8(dataOffset));
32126             },
32127             size: 1,
32128             ascii: true
32129         },
32130         // short, 16 bit int:
32131         3: {
32132             getValue: function (dataView, dataOffset, littleEndian) {
32133                 return dataView.getUint16(dataOffset, littleEndian);
32134             },
32135             size: 2
32136         },
32137         // long, 32 bit int:
32138         4: {
32139             getValue: function (dataView, dataOffset, littleEndian) {
32140                 return dataView.getUint32(dataOffset, littleEndian);
32141             },
32142             size: 4
32143         },
32144         // rational = two long values, first is numerator, second is denominator:
32145         5: {
32146             getValue: function (dataView, dataOffset, littleEndian) {
32147                 return dataView.getUint32(dataOffset, littleEndian) /
32148                     dataView.getUint32(dataOffset + 4, littleEndian);
32149             },
32150             size: 8
32151         },
32152         // slong, 32 bit signed int:
32153         9: {
32154             getValue: function (dataView, dataOffset, littleEndian) {
32155                 return dataView.getInt32(dataOffset, littleEndian);
32156             },
32157             size: 4
32158         },
32159         // srational, two slongs, first is numerator, second is denominator:
32160         10: {
32161             getValue: function (dataView, dataOffset, littleEndian) {
32162                 return dataView.getInt32(dataOffset, littleEndian) /
32163                     dataView.getInt32(dataOffset + 4, littleEndian);
32164             },
32165             size: 8
32166         }
32167     },
32168     
32169     footer : {
32170         STANDARD : [
32171             {
32172                 tag : 'div',
32173                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32174                 action : 'rotate-left',
32175                 cn : [
32176                     {
32177                         tag : 'button',
32178                         cls : 'btn btn-default',
32179                         html : '<i class="fa fa-undo"></i>'
32180                     }
32181                 ]
32182             },
32183             {
32184                 tag : 'div',
32185                 cls : 'btn-group roo-upload-cropbox-picture',
32186                 action : 'picture',
32187                 cn : [
32188                     {
32189                         tag : 'button',
32190                         cls : 'btn btn-default',
32191                         html : '<i class="fa fa-picture-o"></i>'
32192                     }
32193                 ]
32194             },
32195             {
32196                 tag : 'div',
32197                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32198                 action : 'rotate-right',
32199                 cn : [
32200                     {
32201                         tag : 'button',
32202                         cls : 'btn btn-default',
32203                         html : '<i class="fa fa-repeat"></i>'
32204                     }
32205                 ]
32206             }
32207         ],
32208         DOCUMENT : [
32209             {
32210                 tag : 'div',
32211                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32212                 action : 'rotate-left',
32213                 cn : [
32214                     {
32215                         tag : 'button',
32216                         cls : 'btn btn-default',
32217                         html : '<i class="fa fa-undo"></i>'
32218                     }
32219                 ]
32220             },
32221             {
32222                 tag : 'div',
32223                 cls : 'btn-group roo-upload-cropbox-download',
32224                 action : 'download',
32225                 cn : [
32226                     {
32227                         tag : 'button',
32228                         cls : 'btn btn-default',
32229                         html : '<i class="fa fa-download"></i>'
32230                     }
32231                 ]
32232             },
32233             {
32234                 tag : 'div',
32235                 cls : 'btn-group roo-upload-cropbox-crop',
32236                 action : 'crop',
32237                 cn : [
32238                     {
32239                         tag : 'button',
32240                         cls : 'btn btn-default',
32241                         html : '<i class="fa fa-crop"></i>'
32242                     }
32243                 ]
32244             },
32245             {
32246                 tag : 'div',
32247                 cls : 'btn-group roo-upload-cropbox-trash',
32248                 action : 'trash',
32249                 cn : [
32250                     {
32251                         tag : 'button',
32252                         cls : 'btn btn-default',
32253                         html : '<i class="fa fa-trash"></i>'
32254                     }
32255                 ]
32256             },
32257             {
32258                 tag : 'div',
32259                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32260                 action : 'rotate-right',
32261                 cn : [
32262                     {
32263                         tag : 'button',
32264                         cls : 'btn btn-default',
32265                         html : '<i class="fa fa-repeat"></i>'
32266                     }
32267                 ]
32268             }
32269         ],
32270         ROTATOR : [
32271             {
32272                 tag : 'div',
32273                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32274                 action : 'rotate-left',
32275                 cn : [
32276                     {
32277                         tag : 'button',
32278                         cls : 'btn btn-default',
32279                         html : '<i class="fa fa-undo"></i>'
32280                     }
32281                 ]
32282             },
32283             {
32284                 tag : 'div',
32285                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286                 action : 'rotate-right',
32287                 cn : [
32288                     {
32289                         tag : 'button',
32290                         cls : 'btn btn-default',
32291                         html : '<i class="fa fa-repeat"></i>'
32292                     }
32293                 ]
32294             }
32295         ]
32296     }
32297 });
32298
32299 /*
32300 * Licence: LGPL
32301 */
32302
32303 /**
32304  * @class Roo.bootstrap.DocumentManager
32305  * @extends Roo.bootstrap.Component
32306  * Bootstrap DocumentManager class
32307  * @cfg {String} paramName default 'imageUpload'
32308  * @cfg {String} toolTipName default 'filename'
32309  * @cfg {String} method default POST
32310  * @cfg {String} url action url
32311  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32312  * @cfg {Boolean} multiple multiple upload default true
32313  * @cfg {Number} thumbSize default 300
32314  * @cfg {String} fieldLabel
32315  * @cfg {Number} labelWidth default 4
32316  * @cfg {String} labelAlign (left|top) default left
32317  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32318 * @cfg {Number} labellg set the width of label (1-12)
32319  * @cfg {Number} labelmd set the width of label (1-12)
32320  * @cfg {Number} labelsm set the width of label (1-12)
32321  * @cfg {Number} labelxs set the width of label (1-12)
32322  * 
32323  * @constructor
32324  * Create a new DocumentManager
32325  * @param {Object} config The config object
32326  */
32327
32328 Roo.bootstrap.DocumentManager = function(config){
32329     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32330     
32331     this.files = [];
32332     this.delegates = [];
32333     
32334     this.addEvents({
32335         /**
32336          * @event initial
32337          * Fire when initial the DocumentManager
32338          * @param {Roo.bootstrap.DocumentManager} this
32339          */
32340         "initial" : true,
32341         /**
32342          * @event inspect
32343          * inspect selected file
32344          * @param {Roo.bootstrap.DocumentManager} this
32345          * @param {File} file
32346          */
32347         "inspect" : true,
32348         /**
32349          * @event exception
32350          * Fire when xhr load exception
32351          * @param {Roo.bootstrap.DocumentManager} this
32352          * @param {XMLHttpRequest} xhr
32353          */
32354         "exception" : true,
32355         /**
32356          * @event afterupload
32357          * Fire when xhr load exception
32358          * @param {Roo.bootstrap.DocumentManager} this
32359          * @param {XMLHttpRequest} xhr
32360          */
32361         "afterupload" : true,
32362         /**
32363          * @event prepare
32364          * prepare the form data
32365          * @param {Roo.bootstrap.DocumentManager} this
32366          * @param {Object} formData
32367          */
32368         "prepare" : true,
32369         /**
32370          * @event remove
32371          * Fire when remove the file
32372          * @param {Roo.bootstrap.DocumentManager} this
32373          * @param {Object} file
32374          */
32375         "remove" : true,
32376         /**
32377          * @event refresh
32378          * Fire after refresh the file
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          */
32381         "refresh" : true,
32382         /**
32383          * @event click
32384          * Fire after click the image
32385          * @param {Roo.bootstrap.DocumentManager} this
32386          * @param {Object} file
32387          */
32388         "click" : true,
32389         /**
32390          * @event edit
32391          * Fire when upload a image and editable set to true
32392          * @param {Roo.bootstrap.DocumentManager} this
32393          * @param {Object} file
32394          */
32395         "edit" : true,
32396         /**
32397          * @event beforeselectfile
32398          * Fire before select file
32399          * @param {Roo.bootstrap.DocumentManager} this
32400          */
32401         "beforeselectfile" : true,
32402         /**
32403          * @event process
32404          * Fire before process file
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          * @param {Object} file
32407          */
32408         "process" : true,
32409         /**
32410          * @event previewrendered
32411          * Fire when preview rendered
32412          * @param {Roo.bootstrap.DocumentManager} this
32413          * @param {Object} file
32414          */
32415         "previewrendered" : true,
32416         /**
32417          */
32418         "previewResize" : true
32419         
32420     });
32421 };
32422
32423 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32424     
32425     boxes : 0,
32426     inputName : '',
32427     thumbSize : 300,
32428     multiple : true,
32429     files : false,
32430     method : 'POST',
32431     url : '',
32432     paramName : 'imageUpload',
32433     toolTipName : 'filename',
32434     fieldLabel : '',
32435     labelWidth : 4,
32436     labelAlign : 'left',
32437     editable : true,
32438     delegates : false,
32439     xhr : false, 
32440     
32441     labellg : 0,
32442     labelmd : 0,
32443     labelsm : 0,
32444     labelxs : 0,
32445     
32446     getAutoCreate : function()
32447     {   
32448         var managerWidget = {
32449             tag : 'div',
32450             cls : 'roo-document-manager',
32451             cn : [
32452                 {
32453                     tag : 'input',
32454                     cls : 'roo-document-manager-selector',
32455                     type : 'file'
32456                 },
32457                 {
32458                     tag : 'div',
32459                     cls : 'roo-document-manager-uploader',
32460                     cn : [
32461                         {
32462                             tag : 'div',
32463                             cls : 'roo-document-manager-upload-btn',
32464                             html : '<i class="fa fa-plus"></i>'
32465                         }
32466                     ]
32467                     
32468                 }
32469             ]
32470         };
32471         
32472         var content = [
32473             {
32474                 tag : 'div',
32475                 cls : 'column col-md-12',
32476                 cn : managerWidget
32477             }
32478         ];
32479         
32480         if(this.fieldLabel.length){
32481             
32482             content = [
32483                 {
32484                     tag : 'div',
32485                     cls : 'column col-md-12',
32486                     html : this.fieldLabel
32487                 },
32488                 {
32489                     tag : 'div',
32490                     cls : 'column col-md-12',
32491                     cn : managerWidget
32492                 }
32493             ];
32494
32495             if(this.labelAlign == 'left'){
32496                 content = [
32497                     {
32498                         tag : 'div',
32499                         cls : 'column',
32500                         html : this.fieldLabel
32501                     },
32502                     {
32503                         tag : 'div',
32504                         cls : 'column',
32505                         cn : managerWidget
32506                     }
32507                 ];
32508                 
32509                 if(this.labelWidth > 12){
32510                     content[0].style = "width: " + this.labelWidth + 'px';
32511                 }
32512
32513                 if(this.labelWidth < 13 && this.labelmd == 0){
32514                     this.labelmd = this.labelWidth;
32515                 }
32516
32517                 if(this.labellg > 0){
32518                     content[0].cls += ' col-lg-' + this.labellg;
32519                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32520                 }
32521
32522                 if(this.labelmd > 0){
32523                     content[0].cls += ' col-md-' + this.labelmd;
32524                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32525                 }
32526
32527                 if(this.labelsm > 0){
32528                     content[0].cls += ' col-sm-' + this.labelsm;
32529                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32530                 }
32531
32532                 if(this.labelxs > 0){
32533                     content[0].cls += ' col-xs-' + this.labelxs;
32534                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32535                 }
32536                 
32537             }
32538         }
32539         
32540         var cfg = {
32541             tag : 'div',
32542             cls : 'row clearfix',
32543             cn : content
32544         };
32545         
32546         return cfg;
32547         
32548     },
32549     
32550     initEvents : function()
32551     {
32552         this.managerEl = this.el.select('.roo-document-manager', true).first();
32553         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32554         
32555         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32556         this.selectorEl.hide();
32557         
32558         if(this.multiple){
32559             this.selectorEl.attr('multiple', 'multiple');
32560         }
32561         
32562         this.selectorEl.on('change', this.onFileSelected, this);
32563         
32564         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32565         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32566         
32567         this.uploader.on('click', this.onUploaderClick, this);
32568         
32569         this.renderProgressDialog();
32570         
32571         var _this = this;
32572         
32573         window.addEventListener("resize", function() { _this.refresh(); } );
32574         
32575         this.fireEvent('initial', this);
32576     },
32577     
32578     renderProgressDialog : function()
32579     {
32580         var _this = this;
32581         
32582         this.progressDialog = new Roo.bootstrap.Modal({
32583             cls : 'roo-document-manager-progress-dialog',
32584             allow_close : false,
32585             animate : false,
32586             title : '',
32587             buttons : [
32588                 {
32589                     name  :'cancel',
32590                     weight : 'danger',
32591                     html : 'Cancel'
32592                 }
32593             ], 
32594             listeners : { 
32595                 btnclick : function() {
32596                     _this.uploadCancel();
32597                     this.hide();
32598                 }
32599             }
32600         });
32601          
32602         this.progressDialog.render(Roo.get(document.body));
32603          
32604         this.progress = new Roo.bootstrap.Progress({
32605             cls : 'roo-document-manager-progress',
32606             active : true,
32607             striped : true
32608         });
32609         
32610         this.progress.render(this.progressDialog.getChildContainer());
32611         
32612         this.progressBar = new Roo.bootstrap.ProgressBar({
32613             cls : 'roo-document-manager-progress-bar',
32614             aria_valuenow : 0,
32615             aria_valuemin : 0,
32616             aria_valuemax : 12,
32617             panel : 'success'
32618         });
32619         
32620         this.progressBar.render(this.progress.getChildContainer());
32621     },
32622     
32623     onUploaderClick : function(e)
32624     {
32625         e.preventDefault();
32626      
32627         if(this.fireEvent('beforeselectfile', this) != false){
32628             this.selectorEl.dom.click();
32629         }
32630         
32631     },
32632     
32633     onFileSelected : function(e)
32634     {
32635         e.preventDefault();
32636         
32637         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32638             return;
32639         }
32640         
32641         Roo.each(this.selectorEl.dom.files, function(file){
32642             if(this.fireEvent('inspect', this, file) != false){
32643                 this.files.push(file);
32644             }
32645         }, this);
32646         
32647         this.queue();
32648         
32649     },
32650     
32651     queue : function()
32652     {
32653         this.selectorEl.dom.value = '';
32654         
32655         if(!this.files || !this.files.length){
32656             return;
32657         }
32658         
32659         if(this.boxes > 0 && this.files.length > this.boxes){
32660             this.files = this.files.slice(0, this.boxes);
32661         }
32662         
32663         this.uploader.show();
32664         
32665         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32666             this.uploader.hide();
32667         }
32668         
32669         var _this = this;
32670         
32671         var files = [];
32672         
32673         var docs = [];
32674         
32675         Roo.each(this.files, function(file){
32676             
32677             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32678                 var f = this.renderPreview(file);
32679                 files.push(f);
32680                 return;
32681             }
32682             
32683             if(file.type.indexOf('image') != -1){
32684                 this.delegates.push(
32685                     (function(){
32686                         _this.process(file);
32687                     }).createDelegate(this)
32688                 );
32689         
32690                 return;
32691             }
32692             
32693             docs.push(
32694                 (function(){
32695                     _this.process(file);
32696                 }).createDelegate(this)
32697             );
32698             
32699         }, this);
32700         
32701         this.files = files;
32702         
32703         this.delegates = this.delegates.concat(docs);
32704         
32705         if(!this.delegates.length){
32706             this.refresh();
32707             return;
32708         }
32709         
32710         this.progressBar.aria_valuemax = this.delegates.length;
32711         
32712         this.arrange();
32713         
32714         return;
32715     },
32716     
32717     arrange : function()
32718     {
32719         if(!this.delegates.length){
32720             this.progressDialog.hide();
32721             this.refresh();
32722             return;
32723         }
32724         
32725         var delegate = this.delegates.shift();
32726         
32727         this.progressDialog.show();
32728         
32729         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32730         
32731         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32732         
32733         delegate();
32734     },
32735     
32736     refresh : function()
32737     {
32738         this.uploader.show();
32739         
32740         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32741             this.uploader.hide();
32742         }
32743         
32744         Roo.isTouch ? this.closable(false) : this.closable(true);
32745         
32746         this.fireEvent('refresh', this);
32747     },
32748     
32749     onRemove : function(e, el, o)
32750     {
32751         e.preventDefault();
32752         
32753         this.fireEvent('remove', this, o);
32754         
32755     },
32756     
32757     remove : function(o)
32758     {
32759         var files = [];
32760         
32761         Roo.each(this.files, function(file){
32762             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32763                 files.push(file);
32764                 return;
32765             }
32766
32767             o.target.remove();
32768
32769         }, this);
32770         
32771         this.files = files;
32772         
32773         this.refresh();
32774     },
32775     
32776     clear : function()
32777     {
32778         Roo.each(this.files, function(file){
32779             if(!file.target){
32780                 return;
32781             }
32782             
32783             file.target.remove();
32784
32785         }, this);
32786         
32787         this.files = [];
32788         
32789         this.refresh();
32790     },
32791     
32792     onClick : function(e, el, o)
32793     {
32794         e.preventDefault();
32795         
32796         this.fireEvent('click', this, o);
32797         
32798     },
32799     
32800     closable : function(closable)
32801     {
32802         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32803             
32804             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32805             
32806             if(closable){
32807                 el.show();
32808                 return;
32809             }
32810             
32811             el.hide();
32812             
32813         }, this);
32814     },
32815     
32816     xhrOnLoad : function(xhr)
32817     {
32818         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32819             el.remove();
32820         }, this);
32821         
32822         if (xhr.readyState !== 4) {
32823             this.arrange();
32824             this.fireEvent('exception', this, xhr);
32825             return;
32826         }
32827
32828         var response = Roo.decode(xhr.responseText);
32829         
32830         if(!response.success){
32831             this.arrange();
32832             this.fireEvent('exception', this, xhr);
32833             return;
32834         }
32835         
32836         var file = this.renderPreview(response.data);
32837         
32838         this.files.push(file);
32839         
32840         this.arrange();
32841         
32842         this.fireEvent('afterupload', this, xhr);
32843         
32844     },
32845     
32846     xhrOnError : function(xhr)
32847     {
32848         Roo.log('xhr on error');
32849         
32850         var response = Roo.decode(xhr.responseText);
32851           
32852         Roo.log(response);
32853         
32854         this.arrange();
32855     },
32856     
32857     process : function(file)
32858     {
32859         if(this.fireEvent('process', this, file) !== false){
32860             if(this.editable && file.type.indexOf('image') != -1){
32861                 this.fireEvent('edit', this, file);
32862                 return;
32863             }
32864
32865             this.uploadStart(file, false);
32866
32867             return;
32868         }
32869         
32870     },
32871     
32872     uploadStart : function(file, crop)
32873     {
32874         this.xhr = new XMLHttpRequest();
32875         
32876         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32877             this.arrange();
32878             return;
32879         }
32880         
32881         file.xhr = this.xhr;
32882             
32883         this.managerEl.createChild({
32884             tag : 'div',
32885             cls : 'roo-document-manager-loading',
32886             cn : [
32887                 {
32888                     tag : 'div',
32889                     tooltip : file.name,
32890                     cls : 'roo-document-manager-thumb',
32891                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32892                 }
32893             ]
32894
32895         });
32896
32897         this.xhr.open(this.method, this.url, true);
32898         
32899         var headers = {
32900             "Accept": "application/json",
32901             "Cache-Control": "no-cache",
32902             "X-Requested-With": "XMLHttpRequest"
32903         };
32904         
32905         for (var headerName in headers) {
32906             var headerValue = headers[headerName];
32907             if (headerValue) {
32908                 this.xhr.setRequestHeader(headerName, headerValue);
32909             }
32910         }
32911         
32912         var _this = this;
32913         
32914         this.xhr.onload = function()
32915         {
32916             _this.xhrOnLoad(_this.xhr);
32917         }
32918         
32919         this.xhr.onerror = function()
32920         {
32921             _this.xhrOnError(_this.xhr);
32922         }
32923         
32924         var formData = new FormData();
32925
32926         formData.append('returnHTML', 'NO');
32927         
32928         if(crop){
32929             formData.append('crop', crop);
32930         }
32931         
32932         formData.append(this.paramName, file, file.name);
32933         
32934         var options = {
32935             file : file, 
32936             manually : false
32937         };
32938         
32939         if(this.fireEvent('prepare', this, formData, options) != false){
32940             
32941             if(options.manually){
32942                 return;
32943             }
32944             
32945             this.xhr.send(formData);
32946             return;
32947         };
32948         
32949         this.uploadCancel();
32950     },
32951     
32952     uploadCancel : function()
32953     {
32954         if (this.xhr) {
32955             this.xhr.abort();
32956         }
32957         
32958         this.delegates = [];
32959         
32960         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32961             el.remove();
32962         }, this);
32963         
32964         this.arrange();
32965     },
32966     
32967     renderPreview : function(file)
32968     {
32969         if(typeof(file.target) != 'undefined' && file.target){
32970             return file;
32971         }
32972         
32973         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32974         
32975         var previewEl = this.managerEl.createChild({
32976             tag : 'div',
32977             cls : 'roo-document-manager-preview',
32978             cn : [
32979                 {
32980                     tag : 'div',
32981                     tooltip : file[this.toolTipName],
32982                     cls : 'roo-document-manager-thumb',
32983                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32984                 },
32985                 {
32986                     tag : 'button',
32987                     cls : 'close',
32988                     html : '<i class="fa fa-times-circle"></i>'
32989                 }
32990             ]
32991         });
32992
32993         var close = previewEl.select('button.close', true).first();
32994
32995         close.on('click', this.onRemove, this, file);
32996
32997         file.target = previewEl;
32998
32999         var image = previewEl.select('img', true).first();
33000         
33001         var _this = this;
33002         
33003         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33004         
33005         image.on('click', this.onClick, this, file);
33006         
33007         this.fireEvent('previewrendered', this, file);
33008         
33009         return file;
33010         
33011     },
33012     
33013     onPreviewLoad : function(file, image)
33014     {
33015         if(typeof(file.target) == 'undefined' || !file.target){
33016             return;
33017         }
33018         
33019         var width = image.dom.naturalWidth || image.dom.width;
33020         var height = image.dom.naturalHeight || image.dom.height;
33021         
33022         if(!this.previewResize) {
33023             return;
33024         }
33025         
33026         if(width > height){
33027             file.target.addClass('wide');
33028             return;
33029         }
33030         
33031         file.target.addClass('tall');
33032         return;
33033         
33034     },
33035     
33036     uploadFromSource : function(file, crop)
33037     {
33038         this.xhr = new XMLHttpRequest();
33039         
33040         this.managerEl.createChild({
33041             tag : 'div',
33042             cls : 'roo-document-manager-loading',
33043             cn : [
33044                 {
33045                     tag : 'div',
33046                     tooltip : file.name,
33047                     cls : 'roo-document-manager-thumb',
33048                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33049                 }
33050             ]
33051
33052         });
33053
33054         this.xhr.open(this.method, this.url, true);
33055         
33056         var headers = {
33057             "Accept": "application/json",
33058             "Cache-Control": "no-cache",
33059             "X-Requested-With": "XMLHttpRequest"
33060         };
33061         
33062         for (var headerName in headers) {
33063             var headerValue = headers[headerName];
33064             if (headerValue) {
33065                 this.xhr.setRequestHeader(headerName, headerValue);
33066             }
33067         }
33068         
33069         var _this = this;
33070         
33071         this.xhr.onload = function()
33072         {
33073             _this.xhrOnLoad(_this.xhr);
33074         }
33075         
33076         this.xhr.onerror = function()
33077         {
33078             _this.xhrOnError(_this.xhr);
33079         }
33080         
33081         var formData = new FormData();
33082
33083         formData.append('returnHTML', 'NO');
33084         
33085         formData.append('crop', crop);
33086         
33087         if(typeof(file.filename) != 'undefined'){
33088             formData.append('filename', file.filename);
33089         }
33090         
33091         if(typeof(file.mimetype) != 'undefined'){
33092             formData.append('mimetype', file.mimetype);
33093         }
33094         
33095         Roo.log(formData);
33096         
33097         if(this.fireEvent('prepare', this, formData) != false){
33098             this.xhr.send(formData);
33099         };
33100     }
33101 });
33102
33103 /*
33104 * Licence: LGPL
33105 */
33106
33107 /**
33108  * @class Roo.bootstrap.DocumentViewer
33109  * @extends Roo.bootstrap.Component
33110  * Bootstrap DocumentViewer class
33111  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33112  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33113  * 
33114  * @constructor
33115  * Create a new DocumentViewer
33116  * @param {Object} config The config object
33117  */
33118
33119 Roo.bootstrap.DocumentViewer = function(config){
33120     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33121     
33122     this.addEvents({
33123         /**
33124          * @event initial
33125          * Fire after initEvent
33126          * @param {Roo.bootstrap.DocumentViewer} this
33127          */
33128         "initial" : true,
33129         /**
33130          * @event click
33131          * Fire after click
33132          * @param {Roo.bootstrap.DocumentViewer} this
33133          */
33134         "click" : true,
33135         /**
33136          * @event download
33137          * Fire after download button
33138          * @param {Roo.bootstrap.DocumentViewer} this
33139          */
33140         "download" : true,
33141         /**
33142          * @event trash
33143          * Fire after trash button
33144          * @param {Roo.bootstrap.DocumentViewer} this
33145          */
33146         "trash" : true
33147         
33148     });
33149 };
33150
33151 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33152     
33153     showDownload : true,
33154     
33155     showTrash : true,
33156     
33157     getAutoCreate : function()
33158     {
33159         var cfg = {
33160             tag : 'div',
33161             cls : 'roo-document-viewer',
33162             cn : [
33163                 {
33164                     tag : 'div',
33165                     cls : 'roo-document-viewer-body',
33166                     cn : [
33167                         {
33168                             tag : 'div',
33169                             cls : 'roo-document-viewer-thumb',
33170                             cn : [
33171                                 {
33172                                     tag : 'img',
33173                                     cls : 'roo-document-viewer-image'
33174                                 }
33175                             ]
33176                         }
33177                     ]
33178                 },
33179                 {
33180                     tag : 'div',
33181                     cls : 'roo-document-viewer-footer',
33182                     cn : {
33183                         tag : 'div',
33184                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33185                         cn : [
33186                             {
33187                                 tag : 'div',
33188                                 cls : 'btn-group roo-document-viewer-download',
33189                                 cn : [
33190                                     {
33191                                         tag : 'button',
33192                                         cls : 'btn btn-default',
33193                                         html : '<i class="fa fa-download"></i>'
33194                                     }
33195                                 ]
33196                             },
33197                             {
33198                                 tag : 'div',
33199                                 cls : 'btn-group roo-document-viewer-trash',
33200                                 cn : [
33201                                     {
33202                                         tag : 'button',
33203                                         cls : 'btn btn-default',
33204                                         html : '<i class="fa fa-trash"></i>'
33205                                     }
33206                                 ]
33207                             }
33208                         ]
33209                     }
33210                 }
33211             ]
33212         };
33213         
33214         return cfg;
33215     },
33216     
33217     initEvents : function()
33218     {
33219         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33220         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33221         
33222         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33223         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33224         
33225         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33226         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33227         
33228         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33229         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33230         
33231         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33232         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33233         
33234         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33235         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33236         
33237         this.bodyEl.on('click', this.onClick, this);
33238         this.downloadBtn.on('click', this.onDownload, this);
33239         this.trashBtn.on('click', this.onTrash, this);
33240         
33241         this.downloadBtn.hide();
33242         this.trashBtn.hide();
33243         
33244         if(this.showDownload){
33245             this.downloadBtn.show();
33246         }
33247         
33248         if(this.showTrash){
33249             this.trashBtn.show();
33250         }
33251         
33252         if(!this.showDownload && !this.showTrash) {
33253             this.footerEl.hide();
33254         }
33255         
33256     },
33257     
33258     initial : function()
33259     {
33260         this.fireEvent('initial', this);
33261         
33262     },
33263     
33264     onClick : function(e)
33265     {
33266         e.preventDefault();
33267         
33268         this.fireEvent('click', this);
33269     },
33270     
33271     onDownload : function(e)
33272     {
33273         e.preventDefault();
33274         
33275         this.fireEvent('download', this);
33276     },
33277     
33278     onTrash : function(e)
33279     {
33280         e.preventDefault();
33281         
33282         this.fireEvent('trash', this);
33283     }
33284     
33285 });
33286 /*
33287  * - LGPL
33288  *
33289  * nav progress bar
33290  * 
33291  */
33292
33293 /**
33294  * @class Roo.bootstrap.NavProgressBar
33295  * @extends Roo.bootstrap.Component
33296  * Bootstrap NavProgressBar class
33297  * 
33298  * @constructor
33299  * Create a new nav progress bar
33300  * @param {Object} config The config object
33301  */
33302
33303 Roo.bootstrap.NavProgressBar = function(config){
33304     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33305
33306     this.bullets = this.bullets || [];
33307    
33308 //    Roo.bootstrap.NavProgressBar.register(this);
33309      this.addEvents({
33310         /**
33311              * @event changed
33312              * Fires when the active item changes
33313              * @param {Roo.bootstrap.NavProgressBar} this
33314              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33315              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33316          */
33317         'changed': true
33318      });
33319     
33320 };
33321
33322 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33323     
33324     bullets : [],
33325     barItems : [],
33326     
33327     getAutoCreate : function()
33328     {
33329         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33330         
33331         cfg = {
33332             tag : 'div',
33333             cls : 'roo-navigation-bar-group',
33334             cn : [
33335                 {
33336                     tag : 'div',
33337                     cls : 'roo-navigation-top-bar'
33338                 },
33339                 {
33340                     tag : 'div',
33341                     cls : 'roo-navigation-bullets-bar',
33342                     cn : [
33343                         {
33344                             tag : 'ul',
33345                             cls : 'roo-navigation-bar'
33346                         }
33347                     ]
33348                 },
33349                 
33350                 {
33351                     tag : 'div',
33352                     cls : 'roo-navigation-bottom-bar'
33353                 }
33354             ]
33355             
33356         };
33357         
33358         return cfg;
33359         
33360     },
33361     
33362     initEvents: function() 
33363     {
33364         
33365     },
33366     
33367     onRender : function(ct, position) 
33368     {
33369         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33370         
33371         if(this.bullets.length){
33372             Roo.each(this.bullets, function(b){
33373                this.addItem(b);
33374             }, this);
33375         }
33376         
33377         this.format();
33378         
33379     },
33380     
33381     addItem : function(cfg)
33382     {
33383         var item = new Roo.bootstrap.NavProgressItem(cfg);
33384         
33385         item.parentId = this.id;
33386         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33387         
33388         if(cfg.html){
33389             var top = new Roo.bootstrap.Element({
33390                 tag : 'div',
33391                 cls : 'roo-navigation-bar-text'
33392             });
33393             
33394             var bottom = new Roo.bootstrap.Element({
33395                 tag : 'div',
33396                 cls : 'roo-navigation-bar-text'
33397             });
33398             
33399             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33400             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33401             
33402             var topText = new Roo.bootstrap.Element({
33403                 tag : 'span',
33404                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33405             });
33406             
33407             var bottomText = new Roo.bootstrap.Element({
33408                 tag : 'span',
33409                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33410             });
33411             
33412             topText.onRender(top.el, null);
33413             bottomText.onRender(bottom.el, null);
33414             
33415             item.topEl = top;
33416             item.bottomEl = bottom;
33417         }
33418         
33419         this.barItems.push(item);
33420         
33421         return item;
33422     },
33423     
33424     getActive : function()
33425     {
33426         var active = false;
33427         
33428         Roo.each(this.barItems, function(v){
33429             
33430             if (!v.isActive()) {
33431                 return;
33432             }
33433             
33434             active = v;
33435             return false;
33436             
33437         });
33438         
33439         return active;
33440     },
33441     
33442     setActiveItem : function(item)
33443     {
33444         var prev = false;
33445         
33446         Roo.each(this.barItems, function(v){
33447             if (v.rid == item.rid) {
33448                 return ;
33449             }
33450             
33451             if (v.isActive()) {
33452                 v.setActive(false);
33453                 prev = v;
33454             }
33455         });
33456
33457         item.setActive(true);
33458         
33459         this.fireEvent('changed', this, item, prev);
33460     },
33461     
33462     getBarItem: function(rid)
33463     {
33464         var ret = false;
33465         
33466         Roo.each(this.barItems, function(e) {
33467             if (e.rid != rid) {
33468                 return;
33469             }
33470             
33471             ret =  e;
33472             return false;
33473         });
33474         
33475         return ret;
33476     },
33477     
33478     indexOfItem : function(item)
33479     {
33480         var index = false;
33481         
33482         Roo.each(this.barItems, function(v, i){
33483             
33484             if (v.rid != item.rid) {
33485                 return;
33486             }
33487             
33488             index = i;
33489             return false
33490         });
33491         
33492         return index;
33493     },
33494     
33495     setActiveNext : function()
33496     {
33497         var i = this.indexOfItem(this.getActive());
33498         
33499         if (i > this.barItems.length) {
33500             return;
33501         }
33502         
33503         this.setActiveItem(this.barItems[i+1]);
33504     },
33505     
33506     setActivePrev : function()
33507     {
33508         var i = this.indexOfItem(this.getActive());
33509         
33510         if (i  < 1) {
33511             return;
33512         }
33513         
33514         this.setActiveItem(this.barItems[i-1]);
33515     },
33516     
33517     format : function()
33518     {
33519         if(!this.barItems.length){
33520             return;
33521         }
33522      
33523         var width = 100 / this.barItems.length;
33524         
33525         Roo.each(this.barItems, function(i){
33526             i.el.setStyle('width', width + '%');
33527             i.topEl.el.setStyle('width', width + '%');
33528             i.bottomEl.el.setStyle('width', width + '%');
33529         }, this);
33530         
33531     }
33532     
33533 });
33534 /*
33535  * - LGPL
33536  *
33537  * Nav Progress Item
33538  * 
33539  */
33540
33541 /**
33542  * @class Roo.bootstrap.NavProgressItem
33543  * @extends Roo.bootstrap.Component
33544  * Bootstrap NavProgressItem class
33545  * @cfg {String} rid the reference id
33546  * @cfg {Boolean} active (true|false) Is item active default false
33547  * @cfg {Boolean} disabled (true|false) Is item active default false
33548  * @cfg {String} html
33549  * @cfg {String} position (top|bottom) text position default bottom
33550  * @cfg {String} icon show icon instead of number
33551  * 
33552  * @constructor
33553  * Create a new NavProgressItem
33554  * @param {Object} config The config object
33555  */
33556 Roo.bootstrap.NavProgressItem = function(config){
33557     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33558     this.addEvents({
33559         // raw events
33560         /**
33561          * @event click
33562          * The raw click event for the entire grid.
33563          * @param {Roo.bootstrap.NavProgressItem} this
33564          * @param {Roo.EventObject} e
33565          */
33566         "click" : true
33567     });
33568    
33569 };
33570
33571 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33572     
33573     rid : '',
33574     active : false,
33575     disabled : false,
33576     html : '',
33577     position : 'bottom',
33578     icon : false,
33579     
33580     getAutoCreate : function()
33581     {
33582         var iconCls = 'roo-navigation-bar-item-icon';
33583         
33584         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33585         
33586         var cfg = {
33587             tag: 'li',
33588             cls: 'roo-navigation-bar-item',
33589             cn : [
33590                 {
33591                     tag : 'i',
33592                     cls : iconCls
33593                 }
33594             ]
33595         };
33596         
33597         if(this.active){
33598             cfg.cls += ' active';
33599         }
33600         if(this.disabled){
33601             cfg.cls += ' disabled';
33602         }
33603         
33604         return cfg;
33605     },
33606     
33607     disable : function()
33608     {
33609         this.setDisabled(true);
33610     },
33611     
33612     enable : function()
33613     {
33614         this.setDisabled(false);
33615     },
33616     
33617     initEvents: function() 
33618     {
33619         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33620         
33621         this.iconEl.on('click', this.onClick, this);
33622     },
33623     
33624     onClick : function(e)
33625     {
33626         e.preventDefault();
33627         
33628         if(this.disabled){
33629             return;
33630         }
33631         
33632         if(this.fireEvent('click', this, e) === false){
33633             return;
33634         };
33635         
33636         this.parent().setActiveItem(this);
33637     },
33638     
33639     isActive: function () 
33640     {
33641         return this.active;
33642     },
33643     
33644     setActive : function(state)
33645     {
33646         if(this.active == state){
33647             return;
33648         }
33649         
33650         this.active = state;
33651         
33652         if (state) {
33653             this.el.addClass('active');
33654             return;
33655         }
33656         
33657         this.el.removeClass('active');
33658         
33659         return;
33660     },
33661     
33662     setDisabled : function(state)
33663     {
33664         if(this.disabled == state){
33665             return;
33666         }
33667         
33668         this.disabled = state;
33669         
33670         if (state) {
33671             this.el.addClass('disabled');
33672             return;
33673         }
33674         
33675         this.el.removeClass('disabled');
33676     },
33677     
33678     tooltipEl : function()
33679     {
33680         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33681     }
33682 });
33683  
33684
33685  /*
33686  * - LGPL
33687  *
33688  * FieldLabel
33689  * 
33690  */
33691
33692 /**
33693  * @class Roo.bootstrap.FieldLabel
33694  * @extends Roo.bootstrap.Component
33695  * Bootstrap FieldLabel class
33696  * @cfg {String} html contents of the element
33697  * @cfg {String} tag tag of the element default label
33698  * @cfg {String} cls class of the element
33699  * @cfg {String} target label target 
33700  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33701  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33702  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33703  * @cfg {String} iconTooltip default "This field is required"
33704  * @cfg {String} indicatorpos (left|right) default left
33705  * 
33706  * @constructor
33707  * Create a new FieldLabel
33708  * @param {Object} config The config object
33709  */
33710
33711 Roo.bootstrap.FieldLabel = function(config){
33712     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33713     
33714     this.addEvents({
33715             /**
33716              * @event invalid
33717              * Fires after the field has been marked as invalid.
33718              * @param {Roo.form.FieldLabel} this
33719              * @param {String} msg The validation message
33720              */
33721             invalid : true,
33722             /**
33723              * @event valid
33724              * Fires after the field has been validated with no errors.
33725              * @param {Roo.form.FieldLabel} this
33726              */
33727             valid : true
33728         });
33729 };
33730
33731 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33732     
33733     tag: 'label',
33734     cls: '',
33735     html: '',
33736     target: '',
33737     allowBlank : true,
33738     invalidClass : 'has-warning',
33739     validClass : 'has-success',
33740     iconTooltip : 'This field is required',
33741     indicatorpos : 'left',
33742     
33743     getAutoCreate : function(){
33744         
33745         var cls = "";
33746         if (!this.allowBlank) {
33747             cls  = "visible";
33748         }
33749         
33750         var cfg = {
33751             tag : this.tag,
33752             cls : 'roo-bootstrap-field-label ' + this.cls,
33753             for : this.target,
33754             cn : [
33755                 {
33756                     tag : 'i',
33757                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33758                     tooltip : this.iconTooltip
33759                 },
33760                 {
33761                     tag : 'span',
33762                     html : this.html
33763                 }
33764             ] 
33765         };
33766         
33767         if(this.indicatorpos == 'right'){
33768             var cfg = {
33769                 tag : this.tag,
33770                 cls : 'roo-bootstrap-field-label ' + this.cls,
33771                 for : this.target,
33772                 cn : [
33773                     {
33774                         tag : 'span',
33775                         html : this.html
33776                     },
33777                     {
33778                         tag : 'i',
33779                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33780                         tooltip : this.iconTooltip
33781                     }
33782                 ] 
33783             };
33784         }
33785         
33786         return cfg;
33787     },
33788     
33789     initEvents: function() 
33790     {
33791         Roo.bootstrap.Element.superclass.initEvents.call(this);
33792         
33793         this.indicator = this.indicatorEl();
33794         
33795         if(this.indicator){
33796             this.indicator.removeClass('visible');
33797             this.indicator.addClass('invisible');
33798         }
33799         
33800         Roo.bootstrap.FieldLabel.register(this);
33801     },
33802     
33803     indicatorEl : function()
33804     {
33805         var indicator = this.el.select('i.roo-required-indicator',true).first();
33806         
33807         if(!indicator){
33808             return false;
33809         }
33810         
33811         return indicator;
33812         
33813     },
33814     
33815     /**
33816      * Mark this field as valid
33817      */
33818     markValid : function()
33819     {
33820         if(this.indicator){
33821             this.indicator.removeClass('visible');
33822             this.indicator.addClass('invisible');
33823         }
33824         if (Roo.bootstrap.version == 3) {
33825             this.el.removeClass(this.invalidClass);
33826             this.el.addClass(this.validClass);
33827         } else {
33828             this.el.removeClass('is-invalid');
33829             this.el.addClass('is-valid');
33830         }
33831         
33832         
33833         this.fireEvent('valid', this);
33834     },
33835     
33836     /**
33837      * Mark this field as invalid
33838      * @param {String} msg The validation message
33839      */
33840     markInvalid : function(msg)
33841     {
33842         if(this.indicator){
33843             this.indicator.removeClass('invisible');
33844             this.indicator.addClass('visible');
33845         }
33846           if (Roo.bootstrap.version == 3) {
33847             this.el.removeClass(this.validClass);
33848             this.el.addClass(this.invalidClass);
33849         } else {
33850             this.el.removeClass('is-valid');
33851             this.el.addClass('is-invalid');
33852         }
33853         
33854         
33855         this.fireEvent('invalid', this, msg);
33856     }
33857     
33858    
33859 });
33860
33861 Roo.apply(Roo.bootstrap.FieldLabel, {
33862     
33863     groups: {},
33864     
33865      /**
33866     * register a FieldLabel Group
33867     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33868     */
33869     register : function(label)
33870     {
33871         if(this.groups.hasOwnProperty(label.target)){
33872             return;
33873         }
33874      
33875         this.groups[label.target] = label;
33876         
33877     },
33878     /**
33879     * fetch a FieldLabel Group based on the target
33880     * @param {string} target
33881     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33882     */
33883     get: function(target) {
33884         if (typeof(this.groups[target]) == 'undefined') {
33885             return false;
33886         }
33887         
33888         return this.groups[target] ;
33889     }
33890 });
33891
33892  
33893
33894  /*
33895  * - LGPL
33896  *
33897  * page DateSplitField.
33898  * 
33899  */
33900
33901
33902 /**
33903  * @class Roo.bootstrap.DateSplitField
33904  * @extends Roo.bootstrap.Component
33905  * Bootstrap DateSplitField class
33906  * @cfg {string} fieldLabel - the label associated
33907  * @cfg {Number} labelWidth set the width of label (0-12)
33908  * @cfg {String} labelAlign (top|left)
33909  * @cfg {Boolean} dayAllowBlank (true|false) default false
33910  * @cfg {Boolean} monthAllowBlank (true|false) default false
33911  * @cfg {Boolean} yearAllowBlank (true|false) default false
33912  * @cfg {string} dayPlaceholder 
33913  * @cfg {string} monthPlaceholder
33914  * @cfg {string} yearPlaceholder
33915  * @cfg {string} dayFormat default 'd'
33916  * @cfg {string} monthFormat default 'm'
33917  * @cfg {string} yearFormat default 'Y'
33918  * @cfg {Number} labellg set the width of label (1-12)
33919  * @cfg {Number} labelmd set the width of label (1-12)
33920  * @cfg {Number} labelsm set the width of label (1-12)
33921  * @cfg {Number} labelxs set the width of label (1-12)
33922
33923  *     
33924  * @constructor
33925  * Create a new DateSplitField
33926  * @param {Object} config The config object
33927  */
33928
33929 Roo.bootstrap.DateSplitField = function(config){
33930     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33931     
33932     this.addEvents({
33933         // raw events
33934          /**
33935          * @event years
33936          * getting the data of years
33937          * @param {Roo.bootstrap.DateSplitField} this
33938          * @param {Object} years
33939          */
33940         "years" : true,
33941         /**
33942          * @event days
33943          * getting the data of days
33944          * @param {Roo.bootstrap.DateSplitField} this
33945          * @param {Object} days
33946          */
33947         "days" : true,
33948         /**
33949          * @event invalid
33950          * Fires after the field has been marked as invalid.
33951          * @param {Roo.form.Field} this
33952          * @param {String} msg The validation message
33953          */
33954         invalid : true,
33955        /**
33956          * @event valid
33957          * Fires after the field has been validated with no errors.
33958          * @param {Roo.form.Field} this
33959          */
33960         valid : true
33961     });
33962 };
33963
33964 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33965     
33966     fieldLabel : '',
33967     labelAlign : 'top',
33968     labelWidth : 3,
33969     dayAllowBlank : false,
33970     monthAllowBlank : false,
33971     yearAllowBlank : false,
33972     dayPlaceholder : '',
33973     monthPlaceholder : '',
33974     yearPlaceholder : '',
33975     dayFormat : 'd',
33976     monthFormat : 'm',
33977     yearFormat : 'Y',
33978     isFormField : true,
33979     labellg : 0,
33980     labelmd : 0,
33981     labelsm : 0,
33982     labelxs : 0,
33983     
33984     getAutoCreate : function()
33985     {
33986         var cfg = {
33987             tag : 'div',
33988             cls : 'row roo-date-split-field-group',
33989             cn : [
33990                 {
33991                     tag : 'input',
33992                     type : 'hidden',
33993                     cls : 'form-hidden-field roo-date-split-field-group-value',
33994                     name : this.name
33995                 }
33996             ]
33997         };
33998         
33999         var labelCls = 'col-md-12';
34000         var contentCls = 'col-md-4';
34001         
34002         if(this.fieldLabel){
34003             
34004             var label = {
34005                 tag : 'div',
34006                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34007                 cn : [
34008                     {
34009                         tag : 'label',
34010                         html : this.fieldLabel
34011                     }
34012                 ]
34013             };
34014             
34015             if(this.labelAlign == 'left'){
34016             
34017                 if(this.labelWidth > 12){
34018                     label.style = "width: " + this.labelWidth + 'px';
34019                 }
34020
34021                 if(this.labelWidth < 13 && this.labelmd == 0){
34022                     this.labelmd = this.labelWidth;
34023                 }
34024
34025                 if(this.labellg > 0){
34026                     labelCls = ' col-lg-' + this.labellg;
34027                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34028                 }
34029
34030                 if(this.labelmd > 0){
34031                     labelCls = ' col-md-' + this.labelmd;
34032                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34033                 }
34034
34035                 if(this.labelsm > 0){
34036                     labelCls = ' col-sm-' + this.labelsm;
34037                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34038                 }
34039
34040                 if(this.labelxs > 0){
34041                     labelCls = ' col-xs-' + this.labelxs;
34042                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34043                 }
34044             }
34045             
34046             label.cls += ' ' + labelCls;
34047             
34048             cfg.cn.push(label);
34049         }
34050         
34051         Roo.each(['day', 'month', 'year'], function(t){
34052             cfg.cn.push({
34053                 tag : 'div',
34054                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34055             });
34056         }, this);
34057         
34058         return cfg;
34059     },
34060     
34061     inputEl: function ()
34062     {
34063         return this.el.select('.roo-date-split-field-group-value', true).first();
34064     },
34065     
34066     onRender : function(ct, position) 
34067     {
34068         var _this = this;
34069         
34070         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34071         
34072         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34073         
34074         this.dayField = new Roo.bootstrap.ComboBox({
34075             allowBlank : this.dayAllowBlank,
34076             alwaysQuery : true,
34077             displayField : 'value',
34078             editable : false,
34079             fieldLabel : '',
34080             forceSelection : true,
34081             mode : 'local',
34082             placeholder : this.dayPlaceholder,
34083             selectOnFocus : true,
34084             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34085             triggerAction : 'all',
34086             typeAhead : true,
34087             valueField : 'value',
34088             store : new Roo.data.SimpleStore({
34089                 data : (function() {    
34090                     var days = [];
34091                     _this.fireEvent('days', _this, days);
34092                     return days;
34093                 })(),
34094                 fields : [ 'value' ]
34095             }),
34096             listeners : {
34097                 select : function (_self, record, index)
34098                 {
34099                     _this.setValue(_this.getValue());
34100                 }
34101             }
34102         });
34103
34104         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34105         
34106         this.monthField = new Roo.bootstrap.MonthField({
34107             after : '<i class=\"fa fa-calendar\"></i>',
34108             allowBlank : this.monthAllowBlank,
34109             placeholder : this.monthPlaceholder,
34110             readOnly : true,
34111             listeners : {
34112                 render : function (_self)
34113                 {
34114                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34115                         e.preventDefault();
34116                         _self.focus();
34117                     });
34118                 },
34119                 select : function (_self, oldvalue, newvalue)
34120                 {
34121                     _this.setValue(_this.getValue());
34122                 }
34123             }
34124         });
34125         
34126         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34127         
34128         this.yearField = new Roo.bootstrap.ComboBox({
34129             allowBlank : this.yearAllowBlank,
34130             alwaysQuery : true,
34131             displayField : 'value',
34132             editable : false,
34133             fieldLabel : '',
34134             forceSelection : true,
34135             mode : 'local',
34136             placeholder : this.yearPlaceholder,
34137             selectOnFocus : true,
34138             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34139             triggerAction : 'all',
34140             typeAhead : true,
34141             valueField : 'value',
34142             store : new Roo.data.SimpleStore({
34143                 data : (function() {
34144                     var years = [];
34145                     _this.fireEvent('years', _this, years);
34146                     return years;
34147                 })(),
34148                 fields : [ 'value' ]
34149             }),
34150             listeners : {
34151                 select : function (_self, record, index)
34152                 {
34153                     _this.setValue(_this.getValue());
34154                 }
34155             }
34156         });
34157
34158         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34159     },
34160     
34161     setValue : function(v, format)
34162     {
34163         this.inputEl.dom.value = v;
34164         
34165         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34166         
34167         var d = Date.parseDate(v, f);
34168         
34169         if(!d){
34170             this.validate();
34171             return;
34172         }
34173         
34174         this.setDay(d.format(this.dayFormat));
34175         this.setMonth(d.format(this.monthFormat));
34176         this.setYear(d.format(this.yearFormat));
34177         
34178         this.validate();
34179         
34180         return;
34181     },
34182     
34183     setDay : function(v)
34184     {
34185         this.dayField.setValue(v);
34186         this.inputEl.dom.value = this.getValue();
34187         this.validate();
34188         return;
34189     },
34190     
34191     setMonth : function(v)
34192     {
34193         this.monthField.setValue(v, true);
34194         this.inputEl.dom.value = this.getValue();
34195         this.validate();
34196         return;
34197     },
34198     
34199     setYear : function(v)
34200     {
34201         this.yearField.setValue(v);
34202         this.inputEl.dom.value = this.getValue();
34203         this.validate();
34204         return;
34205     },
34206     
34207     getDay : function()
34208     {
34209         return this.dayField.getValue();
34210     },
34211     
34212     getMonth : function()
34213     {
34214         return this.monthField.getValue();
34215     },
34216     
34217     getYear : function()
34218     {
34219         return this.yearField.getValue();
34220     },
34221     
34222     getValue : function()
34223     {
34224         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34225         
34226         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34227         
34228         return date;
34229     },
34230     
34231     reset : function()
34232     {
34233         this.setDay('');
34234         this.setMonth('');
34235         this.setYear('');
34236         this.inputEl.dom.value = '';
34237         this.validate();
34238         return;
34239     },
34240     
34241     validate : function()
34242     {
34243         var d = this.dayField.validate();
34244         var m = this.monthField.validate();
34245         var y = this.yearField.validate();
34246         
34247         var valid = true;
34248         
34249         if(
34250                 (!this.dayAllowBlank && !d) ||
34251                 (!this.monthAllowBlank && !m) ||
34252                 (!this.yearAllowBlank && !y)
34253         ){
34254             valid = false;
34255         }
34256         
34257         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34258             return valid;
34259         }
34260         
34261         if(valid){
34262             this.markValid();
34263             return valid;
34264         }
34265         
34266         this.markInvalid();
34267         
34268         return valid;
34269     },
34270     
34271     markValid : function()
34272     {
34273         
34274         var label = this.el.select('label', true).first();
34275         var icon = this.el.select('i.fa-star', true).first();
34276
34277         if(label && icon){
34278             icon.remove();
34279         }
34280         
34281         this.fireEvent('valid', this);
34282     },
34283     
34284      /**
34285      * Mark this field as invalid
34286      * @param {String} msg The validation message
34287      */
34288     markInvalid : function(msg)
34289     {
34290         
34291         var label = this.el.select('label', true).first();
34292         var icon = this.el.select('i.fa-star', true).first();
34293
34294         if(label && !icon){
34295             this.el.select('.roo-date-split-field-label', true).createChild({
34296                 tag : 'i',
34297                 cls : 'text-danger fa fa-lg fa-star',
34298                 tooltip : 'This field is required',
34299                 style : 'margin-right:5px;'
34300             }, label, true);
34301         }
34302         
34303         this.fireEvent('invalid', this, msg);
34304     },
34305     
34306     clearInvalid : function()
34307     {
34308         var label = this.el.select('label', true).first();
34309         var icon = this.el.select('i.fa-star', true).first();
34310
34311         if(label && icon){
34312             icon.remove();
34313         }
34314         
34315         this.fireEvent('valid', this);
34316     },
34317     
34318     getName: function()
34319     {
34320         return this.name;
34321     }
34322     
34323 });
34324
34325  /**
34326  *
34327  * This is based on 
34328  * http://masonry.desandro.com
34329  *
34330  * The idea is to render all the bricks based on vertical width...
34331  *
34332  * The original code extends 'outlayer' - we might need to use that....
34333  * 
34334  */
34335
34336
34337 /**
34338  * @class Roo.bootstrap.LayoutMasonry
34339  * @extends Roo.bootstrap.Component
34340  * Bootstrap Layout Masonry class
34341  * 
34342  * @constructor
34343  * Create a new Element
34344  * @param {Object} config The config object
34345  */
34346
34347 Roo.bootstrap.LayoutMasonry = function(config){
34348     
34349     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34350     
34351     this.bricks = [];
34352     
34353     Roo.bootstrap.LayoutMasonry.register(this);
34354     
34355     this.addEvents({
34356         // raw events
34357         /**
34358          * @event layout
34359          * Fire after layout the items
34360          * @param {Roo.bootstrap.LayoutMasonry} this
34361          * @param {Roo.EventObject} e
34362          */
34363         "layout" : true
34364     });
34365     
34366 };
34367
34368 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34369     
34370     /**
34371      * @cfg {Boolean} isLayoutInstant = no animation?
34372      */   
34373     isLayoutInstant : false, // needed?
34374    
34375     /**
34376      * @cfg {Number} boxWidth  width of the columns
34377      */   
34378     boxWidth : 450,
34379     
34380       /**
34381      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34382      */   
34383     boxHeight : 0,
34384     
34385     /**
34386      * @cfg {Number} padWidth padding below box..
34387      */   
34388     padWidth : 10, 
34389     
34390     /**
34391      * @cfg {Number} gutter gutter width..
34392      */   
34393     gutter : 10,
34394     
34395      /**
34396      * @cfg {Number} maxCols maximum number of columns
34397      */   
34398     
34399     maxCols: 0,
34400     
34401     /**
34402      * @cfg {Boolean} isAutoInitial defalut true
34403      */   
34404     isAutoInitial : true, 
34405     
34406     containerWidth: 0,
34407     
34408     /**
34409      * @cfg {Boolean} isHorizontal defalut false
34410      */   
34411     isHorizontal : false, 
34412
34413     currentSize : null,
34414     
34415     tag: 'div',
34416     
34417     cls: '',
34418     
34419     bricks: null, //CompositeElement
34420     
34421     cols : 1,
34422     
34423     _isLayoutInited : false,
34424     
34425 //    isAlternative : false, // only use for vertical layout...
34426     
34427     /**
34428      * @cfg {Number} alternativePadWidth padding below box..
34429      */   
34430     alternativePadWidth : 50,
34431     
34432     selectedBrick : [],
34433     
34434     getAutoCreate : function(){
34435         
34436         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34437         
34438         var cfg = {
34439             tag: this.tag,
34440             cls: 'blog-masonary-wrapper ' + this.cls,
34441             cn : {
34442                 cls : 'mas-boxes masonary'
34443             }
34444         };
34445         
34446         return cfg;
34447     },
34448     
34449     getChildContainer: function( )
34450     {
34451         if (this.boxesEl) {
34452             return this.boxesEl;
34453         }
34454         
34455         this.boxesEl = this.el.select('.mas-boxes').first();
34456         
34457         return this.boxesEl;
34458     },
34459     
34460     
34461     initEvents : function()
34462     {
34463         var _this = this;
34464         
34465         if(this.isAutoInitial){
34466             Roo.log('hook children rendered');
34467             this.on('childrenrendered', function() {
34468                 Roo.log('children rendered');
34469                 _this.initial();
34470             } ,this);
34471         }
34472     },
34473     
34474     initial : function()
34475     {
34476         this.selectedBrick = [];
34477         
34478         this.currentSize = this.el.getBox(true);
34479         
34480         Roo.EventManager.onWindowResize(this.resize, this); 
34481
34482         if(!this.isAutoInitial){
34483             this.layout();
34484             return;
34485         }
34486         
34487         this.layout();
34488         
34489         return;
34490         //this.layout.defer(500,this);
34491         
34492     },
34493     
34494     resize : function()
34495     {
34496         var cs = this.el.getBox(true);
34497         
34498         if (
34499                 this.currentSize.width == cs.width && 
34500                 this.currentSize.x == cs.x && 
34501                 this.currentSize.height == cs.height && 
34502                 this.currentSize.y == cs.y 
34503         ) {
34504             Roo.log("no change in with or X or Y");
34505             return;
34506         }
34507         
34508         this.currentSize = cs;
34509         
34510         this.layout();
34511         
34512     },
34513     
34514     layout : function()
34515     {   
34516         this._resetLayout();
34517         
34518         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34519         
34520         this.layoutItems( isInstant );
34521       
34522         this._isLayoutInited = true;
34523         
34524         this.fireEvent('layout', this);
34525         
34526     },
34527     
34528     _resetLayout : function()
34529     {
34530         if(this.isHorizontal){
34531             this.horizontalMeasureColumns();
34532             return;
34533         }
34534         
34535         this.verticalMeasureColumns();
34536         
34537     },
34538     
34539     verticalMeasureColumns : function()
34540     {
34541         this.getContainerWidth();
34542         
34543 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34544 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34545 //            return;
34546 //        }
34547         
34548         var boxWidth = this.boxWidth + this.padWidth;
34549         
34550         if(this.containerWidth < this.boxWidth){
34551             boxWidth = this.containerWidth
34552         }
34553         
34554         var containerWidth = this.containerWidth;
34555         
34556         var cols = Math.floor(containerWidth / boxWidth);
34557         
34558         this.cols = Math.max( cols, 1 );
34559         
34560         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34561         
34562         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34563         
34564         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34565         
34566         this.colWidth = boxWidth + avail - this.padWidth;
34567         
34568         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34569         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34570     },
34571     
34572     horizontalMeasureColumns : function()
34573     {
34574         this.getContainerWidth();
34575         
34576         var boxWidth = this.boxWidth;
34577         
34578         if(this.containerWidth < boxWidth){
34579             boxWidth = this.containerWidth;
34580         }
34581         
34582         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34583         
34584         this.el.setHeight(boxWidth);
34585         
34586     },
34587     
34588     getContainerWidth : function()
34589     {
34590         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34591     },
34592     
34593     layoutItems : function( isInstant )
34594     {
34595         Roo.log(this.bricks);
34596         
34597         var items = Roo.apply([], this.bricks);
34598         
34599         if(this.isHorizontal){
34600             this._horizontalLayoutItems( items , isInstant );
34601             return;
34602         }
34603         
34604 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34605 //            this._verticalAlternativeLayoutItems( items , isInstant );
34606 //            return;
34607 //        }
34608         
34609         this._verticalLayoutItems( items , isInstant );
34610         
34611     },
34612     
34613     _verticalLayoutItems : function ( items , isInstant)
34614     {
34615         if ( !items || !items.length ) {
34616             return;
34617         }
34618         
34619         var standard = [
34620             ['xs', 'xs', 'xs', 'tall'],
34621             ['xs', 'xs', 'tall'],
34622             ['xs', 'xs', 'sm'],
34623             ['xs', 'xs', 'xs'],
34624             ['xs', 'tall'],
34625             ['xs', 'sm'],
34626             ['xs', 'xs'],
34627             ['xs'],
34628             
34629             ['sm', 'xs', 'xs'],
34630             ['sm', 'xs'],
34631             ['sm'],
34632             
34633             ['tall', 'xs', 'xs', 'xs'],
34634             ['tall', 'xs', 'xs'],
34635             ['tall', 'xs'],
34636             ['tall']
34637             
34638         ];
34639         
34640         var queue = [];
34641         
34642         var boxes = [];
34643         
34644         var box = [];
34645         
34646         Roo.each(items, function(item, k){
34647             
34648             switch (item.size) {
34649                 // these layouts take up a full box,
34650                 case 'md' :
34651                 case 'md-left' :
34652                 case 'md-right' :
34653                 case 'wide' :
34654                     
34655                     if(box.length){
34656                         boxes.push(box);
34657                         box = [];
34658                     }
34659                     
34660                     boxes.push([item]);
34661                     
34662                     break;
34663                     
34664                 case 'xs' :
34665                 case 'sm' :
34666                 case 'tall' :
34667                     
34668                     box.push(item);
34669                     
34670                     break;
34671                 default :
34672                     break;
34673                     
34674             }
34675             
34676         }, this);
34677         
34678         if(box.length){
34679             boxes.push(box);
34680             box = [];
34681         }
34682         
34683         var filterPattern = function(box, length)
34684         {
34685             if(!box.length){
34686                 return;
34687             }
34688             
34689             var match = false;
34690             
34691             var pattern = box.slice(0, length);
34692             
34693             var format = [];
34694             
34695             Roo.each(pattern, function(i){
34696                 format.push(i.size);
34697             }, this);
34698             
34699             Roo.each(standard, function(s){
34700                 
34701                 if(String(s) != String(format)){
34702                     return;
34703                 }
34704                 
34705                 match = true;
34706                 return false;
34707                 
34708             }, this);
34709             
34710             if(!match && length == 1){
34711                 return;
34712             }
34713             
34714             if(!match){
34715                 filterPattern(box, length - 1);
34716                 return;
34717             }
34718                 
34719             queue.push(pattern);
34720
34721             box = box.slice(length, box.length);
34722
34723             filterPattern(box, 4);
34724
34725             return;
34726             
34727         }
34728         
34729         Roo.each(boxes, function(box, k){
34730             
34731             if(!box.length){
34732                 return;
34733             }
34734             
34735             if(box.length == 1){
34736                 queue.push(box);
34737                 return;
34738             }
34739             
34740             filterPattern(box, 4);
34741             
34742         }, this);
34743         
34744         this._processVerticalLayoutQueue( queue, isInstant );
34745         
34746     },
34747     
34748 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34749 //    {
34750 //        if ( !items || !items.length ) {
34751 //            return;
34752 //        }
34753 //
34754 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34755 //        
34756 //    },
34757     
34758     _horizontalLayoutItems : function ( items , isInstant)
34759     {
34760         if ( !items || !items.length || items.length < 3) {
34761             return;
34762         }
34763         
34764         items.reverse();
34765         
34766         var eItems = items.slice(0, 3);
34767         
34768         items = items.slice(3, items.length);
34769         
34770         var standard = [
34771             ['xs', 'xs', 'xs', 'wide'],
34772             ['xs', 'xs', 'wide'],
34773             ['xs', 'xs', 'sm'],
34774             ['xs', 'xs', 'xs'],
34775             ['xs', 'wide'],
34776             ['xs', 'sm'],
34777             ['xs', 'xs'],
34778             ['xs'],
34779             
34780             ['sm', 'xs', 'xs'],
34781             ['sm', 'xs'],
34782             ['sm'],
34783             
34784             ['wide', 'xs', 'xs', 'xs'],
34785             ['wide', 'xs', 'xs'],
34786             ['wide', 'xs'],
34787             ['wide'],
34788             
34789             ['wide-thin']
34790         ];
34791         
34792         var queue = [];
34793         
34794         var boxes = [];
34795         
34796         var box = [];
34797         
34798         Roo.each(items, function(item, k){
34799             
34800             switch (item.size) {
34801                 case 'md' :
34802                 case 'md-left' :
34803                 case 'md-right' :
34804                 case 'tall' :
34805                     
34806                     if(box.length){
34807                         boxes.push(box);
34808                         box = [];
34809                     }
34810                     
34811                     boxes.push([item]);
34812                     
34813                     break;
34814                     
34815                 case 'xs' :
34816                 case 'sm' :
34817                 case 'wide' :
34818                 case 'wide-thin' :
34819                     
34820                     box.push(item);
34821                     
34822                     break;
34823                 default :
34824                     break;
34825                     
34826             }
34827             
34828         }, this);
34829         
34830         if(box.length){
34831             boxes.push(box);
34832             box = [];
34833         }
34834         
34835         var filterPattern = function(box, length)
34836         {
34837             if(!box.length){
34838                 return;
34839             }
34840             
34841             var match = false;
34842             
34843             var pattern = box.slice(0, length);
34844             
34845             var format = [];
34846             
34847             Roo.each(pattern, function(i){
34848                 format.push(i.size);
34849             }, this);
34850             
34851             Roo.each(standard, function(s){
34852                 
34853                 if(String(s) != String(format)){
34854                     return;
34855                 }
34856                 
34857                 match = true;
34858                 return false;
34859                 
34860             }, this);
34861             
34862             if(!match && length == 1){
34863                 return;
34864             }
34865             
34866             if(!match){
34867                 filterPattern(box, length - 1);
34868                 return;
34869             }
34870                 
34871             queue.push(pattern);
34872
34873             box = box.slice(length, box.length);
34874
34875             filterPattern(box, 4);
34876
34877             return;
34878             
34879         }
34880         
34881         Roo.each(boxes, function(box, k){
34882             
34883             if(!box.length){
34884                 return;
34885             }
34886             
34887             if(box.length == 1){
34888                 queue.push(box);
34889                 return;
34890             }
34891             
34892             filterPattern(box, 4);
34893             
34894         }, this);
34895         
34896         
34897         var prune = [];
34898         
34899         var pos = this.el.getBox(true);
34900         
34901         var minX = pos.x;
34902         
34903         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34904         
34905         var hit_end = false;
34906         
34907         Roo.each(queue, function(box){
34908             
34909             if(hit_end){
34910                 
34911                 Roo.each(box, function(b){
34912                 
34913                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34914                     b.el.hide();
34915
34916                 }, this);
34917
34918                 return;
34919             }
34920             
34921             var mx = 0;
34922             
34923             Roo.each(box, function(b){
34924                 
34925                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34926                 b.el.show();
34927
34928                 mx = Math.max(mx, b.x);
34929                 
34930             }, this);
34931             
34932             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34933             
34934             if(maxX < minX){
34935                 
34936                 Roo.each(box, function(b){
34937                 
34938                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34939                     b.el.hide();
34940                     
34941                 }, this);
34942                 
34943                 hit_end = true;
34944                 
34945                 return;
34946             }
34947             
34948             prune.push(box);
34949             
34950         }, this);
34951         
34952         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34953     },
34954     
34955     /** Sets position of item in DOM
34956     * @param {Element} item
34957     * @param {Number} x - horizontal position
34958     * @param {Number} y - vertical position
34959     * @param {Boolean} isInstant - disables transitions
34960     */
34961     _processVerticalLayoutQueue : function( queue, isInstant )
34962     {
34963         var pos = this.el.getBox(true);
34964         var x = pos.x;
34965         var y = pos.y;
34966         var maxY = [];
34967         
34968         for (var i = 0; i < this.cols; i++){
34969             maxY[i] = pos.y;
34970         }
34971         
34972         Roo.each(queue, function(box, k){
34973             
34974             var col = k % this.cols;
34975             
34976             Roo.each(box, function(b,kk){
34977                 
34978                 b.el.position('absolute');
34979                 
34980                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34981                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34982                 
34983                 if(b.size == 'md-left' || b.size == 'md-right'){
34984                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34985                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34986                 }
34987                 
34988                 b.el.setWidth(width);
34989                 b.el.setHeight(height);
34990                 // iframe?
34991                 b.el.select('iframe',true).setSize(width,height);
34992                 
34993             }, this);
34994             
34995             for (var i = 0; i < this.cols; i++){
34996                 
34997                 if(maxY[i] < maxY[col]){
34998                     col = i;
34999                     continue;
35000                 }
35001                 
35002                 col = Math.min(col, i);
35003                 
35004             }
35005             
35006             x = pos.x + col * (this.colWidth + this.padWidth);
35007             
35008             y = maxY[col];
35009             
35010             var positions = [];
35011             
35012             switch (box.length){
35013                 case 1 :
35014                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35015                     break;
35016                 case 2 :
35017                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35018                     break;
35019                 case 3 :
35020                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35021                     break;
35022                 case 4 :
35023                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35024                     break;
35025                 default :
35026                     break;
35027             }
35028             
35029             Roo.each(box, function(b,kk){
35030                 
35031                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35032                 
35033                 var sz = b.el.getSize();
35034                 
35035                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35036                 
35037             }, this);
35038             
35039         }, this);
35040         
35041         var mY = 0;
35042         
35043         for (var i = 0; i < this.cols; i++){
35044             mY = Math.max(mY, maxY[i]);
35045         }
35046         
35047         this.el.setHeight(mY - pos.y);
35048         
35049     },
35050     
35051 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35052 //    {
35053 //        var pos = this.el.getBox(true);
35054 //        var x = pos.x;
35055 //        var y = pos.y;
35056 //        var maxX = pos.right;
35057 //        
35058 //        var maxHeight = 0;
35059 //        
35060 //        Roo.each(items, function(item, k){
35061 //            
35062 //            var c = k % 2;
35063 //            
35064 //            item.el.position('absolute');
35065 //                
35066 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35067 //
35068 //            item.el.setWidth(width);
35069 //
35070 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35071 //
35072 //            item.el.setHeight(height);
35073 //            
35074 //            if(c == 0){
35075 //                item.el.setXY([x, y], isInstant ? false : true);
35076 //            } else {
35077 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35078 //            }
35079 //            
35080 //            y = y + height + this.alternativePadWidth;
35081 //            
35082 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35083 //            
35084 //        }, this);
35085 //        
35086 //        this.el.setHeight(maxHeight);
35087 //        
35088 //    },
35089     
35090     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35091     {
35092         var pos = this.el.getBox(true);
35093         
35094         var minX = pos.x;
35095         var minY = pos.y;
35096         
35097         var maxX = pos.right;
35098         
35099         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35100         
35101         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35102         
35103         Roo.each(queue, function(box, k){
35104             
35105             Roo.each(box, function(b, kk){
35106                 
35107                 b.el.position('absolute');
35108                 
35109                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35110                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35111                 
35112                 if(b.size == 'md-left' || b.size == 'md-right'){
35113                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35114                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35115                 }
35116                 
35117                 b.el.setWidth(width);
35118                 b.el.setHeight(height);
35119                 
35120             }, this);
35121             
35122             if(!box.length){
35123                 return;
35124             }
35125             
35126             var positions = [];
35127             
35128             switch (box.length){
35129                 case 1 :
35130                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35131                     break;
35132                 case 2 :
35133                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35134                     break;
35135                 case 3 :
35136                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35137                     break;
35138                 case 4 :
35139                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35140                     break;
35141                 default :
35142                     break;
35143             }
35144             
35145             Roo.each(box, function(b,kk){
35146                 
35147                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35148                 
35149                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35150                 
35151             }, this);
35152             
35153         }, this);
35154         
35155     },
35156     
35157     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35158     {
35159         Roo.each(eItems, function(b,k){
35160             
35161             b.size = (k == 0) ? 'sm' : 'xs';
35162             b.x = (k == 0) ? 2 : 1;
35163             b.y = (k == 0) ? 2 : 1;
35164             
35165             b.el.position('absolute');
35166             
35167             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35168                 
35169             b.el.setWidth(width);
35170             
35171             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35172             
35173             b.el.setHeight(height);
35174             
35175         }, this);
35176
35177         var positions = [];
35178         
35179         positions.push({
35180             x : maxX - this.unitWidth * 2 - this.gutter,
35181             y : minY
35182         });
35183         
35184         positions.push({
35185             x : maxX - this.unitWidth,
35186             y : minY + (this.unitWidth + this.gutter) * 2
35187         });
35188         
35189         positions.push({
35190             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35191             y : minY
35192         });
35193         
35194         Roo.each(eItems, function(b,k){
35195             
35196             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35197
35198         }, this);
35199         
35200     },
35201     
35202     getVerticalOneBoxColPositions : function(x, y, box)
35203     {
35204         var pos = [];
35205         
35206         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35207         
35208         if(box[0].size == 'md-left'){
35209             rand = 0;
35210         }
35211         
35212         if(box[0].size == 'md-right'){
35213             rand = 1;
35214         }
35215         
35216         pos.push({
35217             x : x + (this.unitWidth + this.gutter) * rand,
35218             y : y
35219         });
35220         
35221         return pos;
35222     },
35223     
35224     getVerticalTwoBoxColPositions : function(x, y, box)
35225     {
35226         var pos = [];
35227         
35228         if(box[0].size == 'xs'){
35229             
35230             pos.push({
35231                 x : x,
35232                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35233             });
35234
35235             pos.push({
35236                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35237                 y : y
35238             });
35239             
35240             return pos;
35241             
35242         }
35243         
35244         pos.push({
35245             x : x,
35246             y : y
35247         });
35248
35249         pos.push({
35250             x : x + (this.unitWidth + this.gutter) * 2,
35251             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35252         });
35253         
35254         return pos;
35255         
35256     },
35257     
35258     getVerticalThreeBoxColPositions : function(x, y, box)
35259     {
35260         var pos = [];
35261         
35262         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35263             
35264             pos.push({
35265                 x : x,
35266                 y : y
35267             });
35268
35269             pos.push({
35270                 x : x + (this.unitWidth + this.gutter) * 1,
35271                 y : y
35272             });
35273             
35274             pos.push({
35275                 x : x + (this.unitWidth + this.gutter) * 2,
35276                 y : y
35277             });
35278             
35279             return pos;
35280             
35281         }
35282         
35283         if(box[0].size == 'xs' && box[1].size == 'xs'){
35284             
35285             pos.push({
35286                 x : x,
35287                 y : y
35288             });
35289
35290             pos.push({
35291                 x : x,
35292                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35293             });
35294             
35295             pos.push({
35296                 x : x + (this.unitWidth + this.gutter) * 1,
35297                 y : y
35298             });
35299             
35300             return pos;
35301             
35302         }
35303         
35304         pos.push({
35305             x : x,
35306             y : y
35307         });
35308
35309         pos.push({
35310             x : x + (this.unitWidth + this.gutter) * 2,
35311             y : y
35312         });
35313
35314         pos.push({
35315             x : x + (this.unitWidth + this.gutter) * 2,
35316             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35317         });
35318             
35319         return pos;
35320         
35321     },
35322     
35323     getVerticalFourBoxColPositions : function(x, y, box)
35324     {
35325         var pos = [];
35326         
35327         if(box[0].size == 'xs'){
35328             
35329             pos.push({
35330                 x : x,
35331                 y : y
35332             });
35333
35334             pos.push({
35335                 x : x,
35336                 y : y + (this.unitHeight + this.gutter) * 1
35337             });
35338             
35339             pos.push({
35340                 x : x,
35341                 y : y + (this.unitHeight + this.gutter) * 2
35342             });
35343             
35344             pos.push({
35345                 x : x + (this.unitWidth + this.gutter) * 1,
35346                 y : y
35347             });
35348             
35349             return pos;
35350             
35351         }
35352         
35353         pos.push({
35354             x : x,
35355             y : y
35356         });
35357
35358         pos.push({
35359             x : x + (this.unitWidth + this.gutter) * 2,
35360             y : y
35361         });
35362
35363         pos.push({
35364             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35365             y : y + (this.unitHeight + this.gutter) * 1
35366         });
35367
35368         pos.push({
35369             x : x + (this.unitWidth + this.gutter) * 2,
35370             y : y + (this.unitWidth + this.gutter) * 2
35371         });
35372
35373         return pos;
35374         
35375     },
35376     
35377     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35378     {
35379         var pos = [];
35380         
35381         if(box[0].size == 'md-left'){
35382             pos.push({
35383                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35384                 y : minY
35385             });
35386             
35387             return pos;
35388         }
35389         
35390         if(box[0].size == 'md-right'){
35391             pos.push({
35392                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35393                 y : minY + (this.unitWidth + this.gutter) * 1
35394             });
35395             
35396             return pos;
35397         }
35398         
35399         var rand = Math.floor(Math.random() * (4 - box[0].y));
35400         
35401         pos.push({
35402             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35403             y : minY + (this.unitWidth + this.gutter) * rand
35404         });
35405         
35406         return pos;
35407         
35408     },
35409     
35410     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35411     {
35412         var pos = [];
35413         
35414         if(box[0].size == 'xs'){
35415             
35416             pos.push({
35417                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35418                 y : minY
35419             });
35420
35421             pos.push({
35422                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35423                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35424             });
35425             
35426             return pos;
35427             
35428         }
35429         
35430         pos.push({
35431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35432             y : minY
35433         });
35434
35435         pos.push({
35436             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35437             y : minY + (this.unitWidth + this.gutter) * 2
35438         });
35439         
35440         return pos;
35441         
35442     },
35443     
35444     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35445     {
35446         var pos = [];
35447         
35448         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35449             
35450             pos.push({
35451                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35452                 y : minY
35453             });
35454
35455             pos.push({
35456                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35457                 y : minY + (this.unitWidth + this.gutter) * 1
35458             });
35459             
35460             pos.push({
35461                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35462                 y : minY + (this.unitWidth + this.gutter) * 2
35463             });
35464             
35465             return pos;
35466             
35467         }
35468         
35469         if(box[0].size == 'xs' && box[1].size == 'xs'){
35470             
35471             pos.push({
35472                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35473                 y : minY
35474             });
35475
35476             pos.push({
35477                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35478                 y : minY
35479             });
35480             
35481             pos.push({
35482                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35483                 y : minY + (this.unitWidth + this.gutter) * 1
35484             });
35485             
35486             return pos;
35487             
35488         }
35489         
35490         pos.push({
35491             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35492             y : minY
35493         });
35494
35495         pos.push({
35496             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35497             y : minY + (this.unitWidth + this.gutter) * 2
35498         });
35499
35500         pos.push({
35501             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35502             y : minY + (this.unitWidth + this.gutter) * 2
35503         });
35504             
35505         return pos;
35506         
35507     },
35508     
35509     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35510     {
35511         var pos = [];
35512         
35513         if(box[0].size == 'xs'){
35514             
35515             pos.push({
35516                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35517                 y : minY
35518             });
35519
35520             pos.push({
35521                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35522                 y : minY
35523             });
35524             
35525             pos.push({
35526                 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),
35527                 y : minY
35528             });
35529             
35530             pos.push({
35531                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35532                 y : minY + (this.unitWidth + this.gutter) * 1
35533             });
35534             
35535             return pos;
35536             
35537         }
35538         
35539         pos.push({
35540             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35541             y : minY
35542         });
35543         
35544         pos.push({
35545             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35546             y : minY + (this.unitWidth + this.gutter) * 2
35547         });
35548         
35549         pos.push({
35550             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35551             y : minY + (this.unitWidth + this.gutter) * 2
35552         });
35553         
35554         pos.push({
35555             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),
35556             y : minY + (this.unitWidth + this.gutter) * 2
35557         });
35558
35559         return pos;
35560         
35561     },
35562     
35563     /**
35564     * remove a Masonry Brick
35565     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35566     */
35567     removeBrick : function(brick_id)
35568     {
35569         if (!brick_id) {
35570             return;
35571         }
35572         
35573         for (var i = 0; i<this.bricks.length; i++) {
35574             if (this.bricks[i].id == brick_id) {
35575                 this.bricks.splice(i,1);
35576                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35577                 this.initial();
35578             }
35579         }
35580     },
35581     
35582     /**
35583     * adds a Masonry Brick
35584     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35585     */
35586     addBrick : function(cfg)
35587     {
35588         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35589         //this.register(cn);
35590         cn.parentId = this.id;
35591         cn.render(this.el);
35592         return cn;
35593     },
35594     
35595     /**
35596     * register a Masonry Brick
35597     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35598     */
35599     
35600     register : function(brick)
35601     {
35602         this.bricks.push(brick);
35603         brick.masonryId = this.id;
35604     },
35605     
35606     /**
35607     * clear all the Masonry Brick
35608     */
35609     clearAll : function()
35610     {
35611         this.bricks = [];
35612         //this.getChildContainer().dom.innerHTML = "";
35613         this.el.dom.innerHTML = '';
35614     },
35615     
35616     getSelected : function()
35617     {
35618         if (!this.selectedBrick) {
35619             return false;
35620         }
35621         
35622         return this.selectedBrick;
35623     }
35624 });
35625
35626 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35627     
35628     groups: {},
35629      /**
35630     * register a Masonry Layout
35631     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35632     */
35633     
35634     register : function(layout)
35635     {
35636         this.groups[layout.id] = layout;
35637     },
35638     /**
35639     * fetch a  Masonry Layout based on the masonry layout ID
35640     * @param {string} the masonry layout to add
35641     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35642     */
35643     
35644     get: function(layout_id) {
35645         if (typeof(this.groups[layout_id]) == 'undefined') {
35646             return false;
35647         }
35648         return this.groups[layout_id] ;
35649     }
35650     
35651     
35652     
35653 });
35654
35655  
35656
35657  /**
35658  *
35659  * This is based on 
35660  * http://masonry.desandro.com
35661  *
35662  * The idea is to render all the bricks based on vertical width...
35663  *
35664  * The original code extends 'outlayer' - we might need to use that....
35665  * 
35666  */
35667
35668
35669 /**
35670  * @class Roo.bootstrap.LayoutMasonryAuto
35671  * @extends Roo.bootstrap.Component
35672  * Bootstrap Layout Masonry class
35673  * 
35674  * @constructor
35675  * Create a new Element
35676  * @param {Object} config The config object
35677  */
35678
35679 Roo.bootstrap.LayoutMasonryAuto = function(config){
35680     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35681 };
35682
35683 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35684     
35685       /**
35686      * @cfg {Boolean} isFitWidth  - resize the width..
35687      */   
35688     isFitWidth : false,  // options..
35689     /**
35690      * @cfg {Boolean} isOriginLeft = left align?
35691      */   
35692     isOriginLeft : true,
35693     /**
35694      * @cfg {Boolean} isOriginTop = top align?
35695      */   
35696     isOriginTop : false,
35697     /**
35698      * @cfg {Boolean} isLayoutInstant = no animation?
35699      */   
35700     isLayoutInstant : false, // needed?
35701     /**
35702      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35703      */   
35704     isResizingContainer : true,
35705     /**
35706      * @cfg {Number} columnWidth  width of the columns 
35707      */   
35708     
35709     columnWidth : 0,
35710     
35711     /**
35712      * @cfg {Number} maxCols maximum number of columns
35713      */   
35714     
35715     maxCols: 0,
35716     /**
35717      * @cfg {Number} padHeight padding below box..
35718      */   
35719     
35720     padHeight : 10, 
35721     
35722     /**
35723      * @cfg {Boolean} isAutoInitial defalut true
35724      */   
35725     
35726     isAutoInitial : true, 
35727     
35728     // private?
35729     gutter : 0,
35730     
35731     containerWidth: 0,
35732     initialColumnWidth : 0,
35733     currentSize : null,
35734     
35735     colYs : null, // array.
35736     maxY : 0,
35737     padWidth: 10,
35738     
35739     
35740     tag: 'div',
35741     cls: '',
35742     bricks: null, //CompositeElement
35743     cols : 0, // array?
35744     // element : null, // wrapped now this.el
35745     _isLayoutInited : null, 
35746     
35747     
35748     getAutoCreate : function(){
35749         
35750         var cfg = {
35751             tag: this.tag,
35752             cls: 'blog-masonary-wrapper ' + this.cls,
35753             cn : {
35754                 cls : 'mas-boxes masonary'
35755             }
35756         };
35757         
35758         return cfg;
35759     },
35760     
35761     getChildContainer: function( )
35762     {
35763         if (this.boxesEl) {
35764             return this.boxesEl;
35765         }
35766         
35767         this.boxesEl = this.el.select('.mas-boxes').first();
35768         
35769         return this.boxesEl;
35770     },
35771     
35772     
35773     initEvents : function()
35774     {
35775         var _this = this;
35776         
35777         if(this.isAutoInitial){
35778             Roo.log('hook children rendered');
35779             this.on('childrenrendered', function() {
35780                 Roo.log('children rendered');
35781                 _this.initial();
35782             } ,this);
35783         }
35784         
35785     },
35786     
35787     initial : function()
35788     {
35789         this.reloadItems();
35790
35791         this.currentSize = this.el.getBox(true);
35792
35793         /// was window resize... - let's see if this works..
35794         Roo.EventManager.onWindowResize(this.resize, this); 
35795
35796         if(!this.isAutoInitial){
35797             this.layout();
35798             return;
35799         }
35800         
35801         this.layout.defer(500,this);
35802     },
35803     
35804     reloadItems: function()
35805     {
35806         this.bricks = this.el.select('.masonry-brick', true);
35807         
35808         this.bricks.each(function(b) {
35809             //Roo.log(b.getSize());
35810             if (!b.attr('originalwidth')) {
35811                 b.attr('originalwidth',  b.getSize().width);
35812             }
35813             
35814         });
35815         
35816         Roo.log(this.bricks.elements.length);
35817     },
35818     
35819     resize : function()
35820     {
35821         Roo.log('resize');
35822         var cs = this.el.getBox(true);
35823         
35824         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35825             Roo.log("no change in with or X");
35826             return;
35827         }
35828         this.currentSize = cs;
35829         this.layout();
35830     },
35831     
35832     layout : function()
35833     {
35834          Roo.log('layout');
35835         this._resetLayout();
35836         //this._manageStamps();
35837       
35838         // don't animate first layout
35839         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35840         this.layoutItems( isInstant );
35841       
35842         // flag for initalized
35843         this._isLayoutInited = true;
35844     },
35845     
35846     layoutItems : function( isInstant )
35847     {
35848         //var items = this._getItemsForLayout( this.items );
35849         // original code supports filtering layout items.. we just ignore it..
35850         
35851         this._layoutItems( this.bricks , isInstant );
35852       
35853         this._postLayout();
35854     },
35855     _layoutItems : function ( items , isInstant)
35856     {
35857        //this.fireEvent( 'layout', this, items );
35858     
35859
35860         if ( !items || !items.elements.length ) {
35861           // no items, emit event with empty array
35862             return;
35863         }
35864
35865         var queue = [];
35866         items.each(function(item) {
35867             Roo.log("layout item");
35868             Roo.log(item);
35869             // get x/y object from method
35870             var position = this._getItemLayoutPosition( item );
35871             // enqueue
35872             position.item = item;
35873             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35874             queue.push( position );
35875         }, this);
35876       
35877         this._processLayoutQueue( queue );
35878     },
35879     /** Sets position of item in DOM
35880     * @param {Element} item
35881     * @param {Number} x - horizontal position
35882     * @param {Number} y - vertical position
35883     * @param {Boolean} isInstant - disables transitions
35884     */
35885     _processLayoutQueue : function( queue )
35886     {
35887         for ( var i=0, len = queue.length; i < len; i++ ) {
35888             var obj = queue[i];
35889             obj.item.position('absolute');
35890             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35891         }
35892     },
35893       
35894     
35895     /**
35896     * Any logic you want to do after each layout,
35897     * i.e. size the container
35898     */
35899     _postLayout : function()
35900     {
35901         this.resizeContainer();
35902     },
35903     
35904     resizeContainer : function()
35905     {
35906         if ( !this.isResizingContainer ) {
35907             return;
35908         }
35909         var size = this._getContainerSize();
35910         if ( size ) {
35911             this.el.setSize(size.width,size.height);
35912             this.boxesEl.setSize(size.width,size.height);
35913         }
35914     },
35915     
35916     
35917     
35918     _resetLayout : function()
35919     {
35920         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35921         this.colWidth = this.el.getWidth();
35922         //this.gutter = this.el.getWidth(); 
35923         
35924         this.measureColumns();
35925
35926         // reset column Y
35927         var i = this.cols;
35928         this.colYs = [];
35929         while (i--) {
35930             this.colYs.push( 0 );
35931         }
35932     
35933         this.maxY = 0;
35934     },
35935
35936     measureColumns : function()
35937     {
35938         this.getContainerWidth();
35939       // if columnWidth is 0, default to outerWidth of first item
35940         if ( !this.columnWidth ) {
35941             var firstItem = this.bricks.first();
35942             Roo.log(firstItem);
35943             this.columnWidth  = this.containerWidth;
35944             if (firstItem && firstItem.attr('originalwidth') ) {
35945                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35946             }
35947             // columnWidth fall back to item of first element
35948             Roo.log("set column width?");
35949                         this.initialColumnWidth = this.columnWidth  ;
35950
35951             // if first elem has no width, default to size of container
35952             
35953         }
35954         
35955         
35956         if (this.initialColumnWidth) {
35957             this.columnWidth = this.initialColumnWidth;
35958         }
35959         
35960         
35961             
35962         // column width is fixed at the top - however if container width get's smaller we should
35963         // reduce it...
35964         
35965         // this bit calcs how man columns..
35966             
35967         var columnWidth = this.columnWidth += this.gutter;
35968       
35969         // calculate columns
35970         var containerWidth = this.containerWidth + this.gutter;
35971         
35972         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35973         // fix rounding errors, typically with gutters
35974         var excess = columnWidth - containerWidth % columnWidth;
35975         
35976         
35977         // if overshoot is less than a pixel, round up, otherwise floor it
35978         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35979         cols = Math[ mathMethod ]( cols );
35980         this.cols = Math.max( cols, 1 );
35981         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35982         
35983          // padding positioning..
35984         var totalColWidth = this.cols * this.columnWidth;
35985         var padavail = this.containerWidth - totalColWidth;
35986         // so for 2 columns - we need 3 'pads'
35987         
35988         var padNeeded = (1+this.cols) * this.padWidth;
35989         
35990         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35991         
35992         this.columnWidth += padExtra
35993         //this.padWidth = Math.floor(padavail /  ( this.cols));
35994         
35995         // adjust colum width so that padding is fixed??
35996         
35997         // we have 3 columns ... total = width * 3
35998         // we have X left over... that should be used by 
35999         
36000         //if (this.expandC) {
36001             
36002         //}
36003         
36004         
36005         
36006     },
36007     
36008     getContainerWidth : function()
36009     {
36010        /* // container is parent if fit width
36011         var container = this.isFitWidth ? this.element.parentNode : this.element;
36012         // check that this.size and size are there
36013         // IE8 triggers resize on body size change, so they might not be
36014         
36015         var size = getSize( container );  //FIXME
36016         this.containerWidth = size && size.innerWidth; //FIXME
36017         */
36018          
36019         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36020         
36021     },
36022     
36023     _getItemLayoutPosition : function( item )  // what is item?
36024     {
36025         // we resize the item to our columnWidth..
36026       
36027         item.setWidth(this.columnWidth);
36028         item.autoBoxAdjust  = false;
36029         
36030         var sz = item.getSize();
36031  
36032         // how many columns does this brick span
36033         var remainder = this.containerWidth % this.columnWidth;
36034         
36035         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36036         // round if off by 1 pixel, otherwise use ceil
36037         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36038         colSpan = Math.min( colSpan, this.cols );
36039         
36040         // normally this should be '1' as we dont' currently allow multi width columns..
36041         
36042         var colGroup = this._getColGroup( colSpan );
36043         // get the minimum Y value from the columns
36044         var minimumY = Math.min.apply( Math, colGroup );
36045         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36046         
36047         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36048          
36049         // position the brick
36050         var position = {
36051             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36052             y: this.currentSize.y + minimumY + this.padHeight
36053         };
36054         
36055         Roo.log(position);
36056         // apply setHeight to necessary columns
36057         var setHeight = minimumY + sz.height + this.padHeight;
36058         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36059         
36060         var setSpan = this.cols + 1 - colGroup.length;
36061         for ( var i = 0; i < setSpan; i++ ) {
36062           this.colYs[ shortColIndex + i ] = setHeight ;
36063         }
36064       
36065         return position;
36066     },
36067     
36068     /**
36069      * @param {Number} colSpan - number of columns the element spans
36070      * @returns {Array} colGroup
36071      */
36072     _getColGroup : function( colSpan )
36073     {
36074         if ( colSpan < 2 ) {
36075           // if brick spans only one column, use all the column Ys
36076           return this.colYs;
36077         }
36078       
36079         var colGroup = [];
36080         // how many different places could this brick fit horizontally
36081         var groupCount = this.cols + 1 - colSpan;
36082         // for each group potential horizontal position
36083         for ( var i = 0; i < groupCount; i++ ) {
36084           // make an array of colY values for that one group
36085           var groupColYs = this.colYs.slice( i, i + colSpan );
36086           // and get the max value of the array
36087           colGroup[i] = Math.max.apply( Math, groupColYs );
36088         }
36089         return colGroup;
36090     },
36091     /*
36092     _manageStamp : function( stamp )
36093     {
36094         var stampSize =  stamp.getSize();
36095         var offset = stamp.getBox();
36096         // get the columns that this stamp affects
36097         var firstX = this.isOriginLeft ? offset.x : offset.right;
36098         var lastX = firstX + stampSize.width;
36099         var firstCol = Math.floor( firstX / this.columnWidth );
36100         firstCol = Math.max( 0, firstCol );
36101         
36102         var lastCol = Math.floor( lastX / this.columnWidth );
36103         // lastCol should not go over if multiple of columnWidth #425
36104         lastCol -= lastX % this.columnWidth ? 0 : 1;
36105         lastCol = Math.min( this.cols - 1, lastCol );
36106         
36107         // set colYs to bottom of the stamp
36108         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36109             stampSize.height;
36110             
36111         for ( var i = firstCol; i <= lastCol; i++ ) {
36112           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36113         }
36114     },
36115     */
36116     
36117     _getContainerSize : function()
36118     {
36119         this.maxY = Math.max.apply( Math, this.colYs );
36120         var size = {
36121             height: this.maxY
36122         };
36123       
36124         if ( this.isFitWidth ) {
36125             size.width = this._getContainerFitWidth();
36126         }
36127       
36128         return size;
36129     },
36130     
36131     _getContainerFitWidth : function()
36132     {
36133         var unusedCols = 0;
36134         // count unused columns
36135         var i = this.cols;
36136         while ( --i ) {
36137           if ( this.colYs[i] !== 0 ) {
36138             break;
36139           }
36140           unusedCols++;
36141         }
36142         // fit container to columns that have been used
36143         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36144     },
36145     
36146     needsResizeLayout : function()
36147     {
36148         var previousWidth = this.containerWidth;
36149         this.getContainerWidth();
36150         return previousWidth !== this.containerWidth;
36151     }
36152  
36153 });
36154
36155  
36156
36157  /*
36158  * - LGPL
36159  *
36160  * element
36161  * 
36162  */
36163
36164 /**
36165  * @class Roo.bootstrap.MasonryBrick
36166  * @extends Roo.bootstrap.Component
36167  * Bootstrap MasonryBrick class
36168  * 
36169  * @constructor
36170  * Create a new MasonryBrick
36171  * @param {Object} config The config object
36172  */
36173
36174 Roo.bootstrap.MasonryBrick = function(config){
36175     
36176     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36177     
36178     Roo.bootstrap.MasonryBrick.register(this);
36179     
36180     this.addEvents({
36181         // raw events
36182         /**
36183          * @event click
36184          * When a MasonryBrick is clcik
36185          * @param {Roo.bootstrap.MasonryBrick} this
36186          * @param {Roo.EventObject} e
36187          */
36188         "click" : true
36189     });
36190 };
36191
36192 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36193     
36194     /**
36195      * @cfg {String} title
36196      */   
36197     title : '',
36198     /**
36199      * @cfg {String} html
36200      */   
36201     html : '',
36202     /**
36203      * @cfg {String} bgimage
36204      */   
36205     bgimage : '',
36206     /**
36207      * @cfg {String} videourl
36208      */   
36209     videourl : '',
36210     /**
36211      * @cfg {String} cls
36212      */   
36213     cls : '',
36214     /**
36215      * @cfg {String} href
36216      */   
36217     href : '',
36218     /**
36219      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36220      */   
36221     size : 'xs',
36222     
36223     /**
36224      * @cfg {String} placetitle (center|bottom)
36225      */   
36226     placetitle : '',
36227     
36228     /**
36229      * @cfg {Boolean} isFitContainer defalut true
36230      */   
36231     isFitContainer : true, 
36232     
36233     /**
36234      * @cfg {Boolean} preventDefault defalut false
36235      */   
36236     preventDefault : false, 
36237     
36238     /**
36239      * @cfg {Boolean} inverse defalut false
36240      */   
36241     maskInverse : false, 
36242     
36243     getAutoCreate : function()
36244     {
36245         if(!this.isFitContainer){
36246             return this.getSplitAutoCreate();
36247         }
36248         
36249         var cls = 'masonry-brick masonry-brick-full';
36250         
36251         if(this.href.length){
36252             cls += ' masonry-brick-link';
36253         }
36254         
36255         if(this.bgimage.length){
36256             cls += ' masonry-brick-image';
36257         }
36258         
36259         if(this.maskInverse){
36260             cls += ' mask-inverse';
36261         }
36262         
36263         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36264             cls += ' enable-mask';
36265         }
36266         
36267         if(this.size){
36268             cls += ' masonry-' + this.size + '-brick';
36269         }
36270         
36271         if(this.placetitle.length){
36272             
36273             switch (this.placetitle) {
36274                 case 'center' :
36275                     cls += ' masonry-center-title';
36276                     break;
36277                 case 'bottom' :
36278                     cls += ' masonry-bottom-title';
36279                     break;
36280                 default:
36281                     break;
36282             }
36283             
36284         } else {
36285             if(!this.html.length && !this.bgimage.length){
36286                 cls += ' masonry-center-title';
36287             }
36288
36289             if(!this.html.length && this.bgimage.length){
36290                 cls += ' masonry-bottom-title';
36291             }
36292         }
36293         
36294         if(this.cls){
36295             cls += ' ' + this.cls;
36296         }
36297         
36298         var cfg = {
36299             tag: (this.href.length) ? 'a' : 'div',
36300             cls: cls,
36301             cn: [
36302                 {
36303                     tag: 'div',
36304                     cls: 'masonry-brick-mask'
36305                 },
36306                 {
36307                     tag: 'div',
36308                     cls: 'masonry-brick-paragraph',
36309                     cn: []
36310                 }
36311             ]
36312         };
36313         
36314         if(this.href.length){
36315             cfg.href = this.href;
36316         }
36317         
36318         var cn = cfg.cn[1].cn;
36319         
36320         if(this.title.length){
36321             cn.push({
36322                 tag: 'h4',
36323                 cls: 'masonry-brick-title',
36324                 html: this.title
36325             });
36326         }
36327         
36328         if(this.html.length){
36329             cn.push({
36330                 tag: 'p',
36331                 cls: 'masonry-brick-text',
36332                 html: this.html
36333             });
36334         }
36335         
36336         if (!this.title.length && !this.html.length) {
36337             cfg.cn[1].cls += ' hide';
36338         }
36339         
36340         if(this.bgimage.length){
36341             cfg.cn.push({
36342                 tag: 'img',
36343                 cls: 'masonry-brick-image-view',
36344                 src: this.bgimage
36345             });
36346         }
36347         
36348         if(this.videourl.length){
36349             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36350             // youtube support only?
36351             cfg.cn.push({
36352                 tag: 'iframe',
36353                 cls: 'masonry-brick-image-view',
36354                 src: vurl,
36355                 frameborder : 0,
36356                 allowfullscreen : true
36357             });
36358         }
36359         
36360         return cfg;
36361         
36362     },
36363     
36364     getSplitAutoCreate : function()
36365     {
36366         var cls = 'masonry-brick masonry-brick-split';
36367         
36368         if(this.href.length){
36369             cls += ' masonry-brick-link';
36370         }
36371         
36372         if(this.bgimage.length){
36373             cls += ' masonry-brick-image';
36374         }
36375         
36376         if(this.size){
36377             cls += ' masonry-' + this.size + '-brick';
36378         }
36379         
36380         switch (this.placetitle) {
36381             case 'center' :
36382                 cls += ' masonry-center-title';
36383                 break;
36384             case 'bottom' :
36385                 cls += ' masonry-bottom-title';
36386                 break;
36387             default:
36388                 if(!this.bgimage.length){
36389                     cls += ' masonry-center-title';
36390                 }
36391
36392                 if(this.bgimage.length){
36393                     cls += ' masonry-bottom-title';
36394                 }
36395                 break;
36396         }
36397         
36398         if(this.cls){
36399             cls += ' ' + this.cls;
36400         }
36401         
36402         var cfg = {
36403             tag: (this.href.length) ? 'a' : 'div',
36404             cls: cls,
36405             cn: [
36406                 {
36407                     tag: 'div',
36408                     cls: 'masonry-brick-split-head',
36409                     cn: [
36410                         {
36411                             tag: 'div',
36412                             cls: 'masonry-brick-paragraph',
36413                             cn: []
36414                         }
36415                     ]
36416                 },
36417                 {
36418                     tag: 'div',
36419                     cls: 'masonry-brick-split-body',
36420                     cn: []
36421                 }
36422             ]
36423         };
36424         
36425         if(this.href.length){
36426             cfg.href = this.href;
36427         }
36428         
36429         if(this.title.length){
36430             cfg.cn[0].cn[0].cn.push({
36431                 tag: 'h4',
36432                 cls: 'masonry-brick-title',
36433                 html: this.title
36434             });
36435         }
36436         
36437         if(this.html.length){
36438             cfg.cn[1].cn.push({
36439                 tag: 'p',
36440                 cls: 'masonry-brick-text',
36441                 html: this.html
36442             });
36443         }
36444
36445         if(this.bgimage.length){
36446             cfg.cn[0].cn.push({
36447                 tag: 'img',
36448                 cls: 'masonry-brick-image-view',
36449                 src: this.bgimage
36450             });
36451         }
36452         
36453         if(this.videourl.length){
36454             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36455             // youtube support only?
36456             cfg.cn[0].cn.cn.push({
36457                 tag: 'iframe',
36458                 cls: 'masonry-brick-image-view',
36459                 src: vurl,
36460                 frameborder : 0,
36461                 allowfullscreen : true
36462             });
36463         }
36464         
36465         return cfg;
36466     },
36467     
36468     initEvents: function() 
36469     {
36470         switch (this.size) {
36471             case 'xs' :
36472                 this.x = 1;
36473                 this.y = 1;
36474                 break;
36475             case 'sm' :
36476                 this.x = 2;
36477                 this.y = 2;
36478                 break;
36479             case 'md' :
36480             case 'md-left' :
36481             case 'md-right' :
36482                 this.x = 3;
36483                 this.y = 3;
36484                 break;
36485             case 'tall' :
36486                 this.x = 2;
36487                 this.y = 3;
36488                 break;
36489             case 'wide' :
36490                 this.x = 3;
36491                 this.y = 2;
36492                 break;
36493             case 'wide-thin' :
36494                 this.x = 3;
36495                 this.y = 1;
36496                 break;
36497                         
36498             default :
36499                 break;
36500         }
36501         
36502         if(Roo.isTouch){
36503             this.el.on('touchstart', this.onTouchStart, this);
36504             this.el.on('touchmove', this.onTouchMove, this);
36505             this.el.on('touchend', this.onTouchEnd, this);
36506             this.el.on('contextmenu', this.onContextMenu, this);
36507         } else {
36508             this.el.on('mouseenter'  ,this.enter, this);
36509             this.el.on('mouseleave', this.leave, this);
36510             this.el.on('click', this.onClick, this);
36511         }
36512         
36513         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36514             this.parent().bricks.push(this);   
36515         }
36516         
36517     },
36518     
36519     onClick: function(e, el)
36520     {
36521         var time = this.endTimer - this.startTimer;
36522         // Roo.log(e.preventDefault());
36523         if(Roo.isTouch){
36524             if(time > 1000){
36525                 e.preventDefault();
36526                 return;
36527             }
36528         }
36529         
36530         if(!this.preventDefault){
36531             return;
36532         }
36533         
36534         e.preventDefault();
36535         
36536         if (this.activeClass != '') {
36537             this.selectBrick();
36538         }
36539         
36540         this.fireEvent('click', this, e);
36541     },
36542     
36543     enter: function(e, el)
36544     {
36545         e.preventDefault();
36546         
36547         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36548             return;
36549         }
36550         
36551         if(this.bgimage.length && this.html.length){
36552             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36553         }
36554     },
36555     
36556     leave: function(e, el)
36557     {
36558         e.preventDefault();
36559         
36560         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36561             return;
36562         }
36563         
36564         if(this.bgimage.length && this.html.length){
36565             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36566         }
36567     },
36568     
36569     onTouchStart: function(e, el)
36570     {
36571 //        e.preventDefault();
36572         
36573         this.touchmoved = false;
36574         
36575         if(!this.isFitContainer){
36576             return;
36577         }
36578         
36579         if(!this.bgimage.length || !this.html.length){
36580             return;
36581         }
36582         
36583         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36584         
36585         this.timer = new Date().getTime();
36586         
36587     },
36588     
36589     onTouchMove: function(e, el)
36590     {
36591         this.touchmoved = true;
36592     },
36593     
36594     onContextMenu : function(e,el)
36595     {
36596         e.preventDefault();
36597         e.stopPropagation();
36598         return false;
36599     },
36600     
36601     onTouchEnd: function(e, el)
36602     {
36603 //        e.preventDefault();
36604         
36605         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36606         
36607             this.leave(e,el);
36608             
36609             return;
36610         }
36611         
36612         if(!this.bgimage.length || !this.html.length){
36613             
36614             if(this.href.length){
36615                 window.location.href = this.href;
36616             }
36617             
36618             return;
36619         }
36620         
36621         if(!this.isFitContainer){
36622             return;
36623         }
36624         
36625         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36626         
36627         window.location.href = this.href;
36628     },
36629     
36630     //selection on single brick only
36631     selectBrick : function() {
36632         
36633         if (!this.parentId) {
36634             return;
36635         }
36636         
36637         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36638         var index = m.selectedBrick.indexOf(this.id);
36639         
36640         if ( index > -1) {
36641             m.selectedBrick.splice(index,1);
36642             this.el.removeClass(this.activeClass);
36643             return;
36644         }
36645         
36646         for(var i = 0; i < m.selectedBrick.length; i++) {
36647             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36648             b.el.removeClass(b.activeClass);
36649         }
36650         
36651         m.selectedBrick = [];
36652         
36653         m.selectedBrick.push(this.id);
36654         this.el.addClass(this.activeClass);
36655         return;
36656     },
36657     
36658     isSelected : function(){
36659         return this.el.hasClass(this.activeClass);
36660         
36661     }
36662 });
36663
36664 Roo.apply(Roo.bootstrap.MasonryBrick, {
36665     
36666     //groups: {},
36667     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36668      /**
36669     * register a Masonry Brick
36670     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36671     */
36672     
36673     register : function(brick)
36674     {
36675         //this.groups[brick.id] = brick;
36676         this.groups.add(brick.id, brick);
36677     },
36678     /**
36679     * fetch a  masonry brick based on the masonry brick ID
36680     * @param {string} the masonry brick to add
36681     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36682     */
36683     
36684     get: function(brick_id) 
36685     {
36686         // if (typeof(this.groups[brick_id]) == 'undefined') {
36687         //     return false;
36688         // }
36689         // return this.groups[brick_id] ;
36690         
36691         if(this.groups.key(brick_id)) {
36692             return this.groups.key(brick_id);
36693         }
36694         
36695         return false;
36696     }
36697     
36698     
36699     
36700 });
36701
36702  /*
36703  * - LGPL
36704  *
36705  * element
36706  * 
36707  */
36708
36709 /**
36710  * @class Roo.bootstrap.Brick
36711  * @extends Roo.bootstrap.Component
36712  * Bootstrap Brick class
36713  * 
36714  * @constructor
36715  * Create a new Brick
36716  * @param {Object} config The config object
36717  */
36718
36719 Roo.bootstrap.Brick = function(config){
36720     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36721     
36722     this.addEvents({
36723         // raw events
36724         /**
36725          * @event click
36726          * When a Brick is click
36727          * @param {Roo.bootstrap.Brick} this
36728          * @param {Roo.EventObject} e
36729          */
36730         "click" : true
36731     });
36732 };
36733
36734 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36735     
36736     /**
36737      * @cfg {String} title
36738      */   
36739     title : '',
36740     /**
36741      * @cfg {String} html
36742      */   
36743     html : '',
36744     /**
36745      * @cfg {String} bgimage
36746      */   
36747     bgimage : '',
36748     /**
36749      * @cfg {String} cls
36750      */   
36751     cls : '',
36752     /**
36753      * @cfg {String} href
36754      */   
36755     href : '',
36756     /**
36757      * @cfg {String} video
36758      */   
36759     video : '',
36760     /**
36761      * @cfg {Boolean} square
36762      */   
36763     square : true,
36764     
36765     getAutoCreate : function()
36766     {
36767         var cls = 'roo-brick';
36768         
36769         if(this.href.length){
36770             cls += ' roo-brick-link';
36771         }
36772         
36773         if(this.bgimage.length){
36774             cls += ' roo-brick-image';
36775         }
36776         
36777         if(!this.html.length && !this.bgimage.length){
36778             cls += ' roo-brick-center-title';
36779         }
36780         
36781         if(!this.html.length && this.bgimage.length){
36782             cls += ' roo-brick-bottom-title';
36783         }
36784         
36785         if(this.cls){
36786             cls += ' ' + this.cls;
36787         }
36788         
36789         var cfg = {
36790             tag: (this.href.length) ? 'a' : 'div',
36791             cls: cls,
36792             cn: [
36793                 {
36794                     tag: 'div',
36795                     cls: 'roo-brick-paragraph',
36796                     cn: []
36797                 }
36798             ]
36799         };
36800         
36801         if(this.href.length){
36802             cfg.href = this.href;
36803         }
36804         
36805         var cn = cfg.cn[0].cn;
36806         
36807         if(this.title.length){
36808             cn.push({
36809                 tag: 'h4',
36810                 cls: 'roo-brick-title',
36811                 html: this.title
36812             });
36813         }
36814         
36815         if(this.html.length){
36816             cn.push({
36817                 tag: 'p',
36818                 cls: 'roo-brick-text',
36819                 html: this.html
36820             });
36821         } else {
36822             cn.cls += ' hide';
36823         }
36824         
36825         if(this.bgimage.length){
36826             cfg.cn.push({
36827                 tag: 'img',
36828                 cls: 'roo-brick-image-view',
36829                 src: this.bgimage
36830             });
36831         }
36832         
36833         return cfg;
36834     },
36835     
36836     initEvents: function() 
36837     {
36838         if(this.title.length || this.html.length){
36839             this.el.on('mouseenter'  ,this.enter, this);
36840             this.el.on('mouseleave', this.leave, this);
36841         }
36842         
36843         Roo.EventManager.onWindowResize(this.resize, this); 
36844         
36845         if(this.bgimage.length){
36846             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36847             this.imageEl.on('load', this.onImageLoad, this);
36848             return;
36849         }
36850         
36851         this.resize();
36852     },
36853     
36854     onImageLoad : function()
36855     {
36856         this.resize();
36857     },
36858     
36859     resize : function()
36860     {
36861         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36862         
36863         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36864         
36865         if(this.bgimage.length){
36866             var image = this.el.select('.roo-brick-image-view', true).first();
36867             
36868             image.setWidth(paragraph.getWidth());
36869             
36870             if(this.square){
36871                 image.setHeight(paragraph.getWidth());
36872             }
36873             
36874             this.el.setHeight(image.getHeight());
36875             paragraph.setHeight(image.getHeight());
36876             
36877         }
36878         
36879     },
36880     
36881     enter: function(e, el)
36882     {
36883         e.preventDefault();
36884         
36885         if(this.bgimage.length){
36886             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36887             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36888         }
36889     },
36890     
36891     leave: function(e, el)
36892     {
36893         e.preventDefault();
36894         
36895         if(this.bgimage.length){
36896             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36897             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36898         }
36899     }
36900     
36901 });
36902
36903  
36904
36905  /*
36906  * - LGPL
36907  *
36908  * Number field 
36909  */
36910
36911 /**
36912  * @class Roo.bootstrap.NumberField
36913  * @extends Roo.bootstrap.Input
36914  * Bootstrap NumberField class
36915  * 
36916  * 
36917  * 
36918  * 
36919  * @constructor
36920  * Create a new NumberField
36921  * @param {Object} config The config object
36922  */
36923
36924 Roo.bootstrap.NumberField = function(config){
36925     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36926 };
36927
36928 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36929     
36930     /**
36931      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36932      */
36933     allowDecimals : true,
36934     /**
36935      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36936      */
36937     decimalSeparator : ".",
36938     /**
36939      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36940      */
36941     decimalPrecision : 2,
36942     /**
36943      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36944      */
36945     allowNegative : true,
36946     
36947     /**
36948      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36949      */
36950     allowZero: true,
36951     /**
36952      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36953      */
36954     minValue : Number.NEGATIVE_INFINITY,
36955     /**
36956      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36957      */
36958     maxValue : Number.MAX_VALUE,
36959     /**
36960      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36961      */
36962     minText : "The minimum value for this field is {0}",
36963     /**
36964      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36965      */
36966     maxText : "The maximum value for this field is {0}",
36967     /**
36968      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36969      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36970      */
36971     nanText : "{0} is not a valid number",
36972     /**
36973      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36974      */
36975     thousandsDelimiter : false,
36976     /**
36977      * @cfg {String} valueAlign alignment of value
36978      */
36979     valueAlign : "left",
36980
36981     getAutoCreate : function()
36982     {
36983         var hiddenInput = {
36984             tag: 'input',
36985             type: 'hidden',
36986             id: Roo.id(),
36987             cls: 'hidden-number-input'
36988         };
36989         
36990         if (this.name) {
36991             hiddenInput.name = this.name;
36992         }
36993         
36994         this.name = '';
36995         
36996         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36997         
36998         this.name = hiddenInput.name;
36999         
37000         if(cfg.cn.length > 0) {
37001             cfg.cn.push(hiddenInput);
37002         }
37003         
37004         return cfg;
37005     },
37006
37007     // private
37008     initEvents : function()
37009     {   
37010         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37011         
37012         var allowed = "0123456789";
37013         
37014         if(this.allowDecimals){
37015             allowed += this.decimalSeparator;
37016         }
37017         
37018         if(this.allowNegative){
37019             allowed += "-";
37020         }
37021         
37022         if(this.thousandsDelimiter) {
37023             allowed += ",";
37024         }
37025         
37026         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37027         
37028         var keyPress = function(e){
37029             
37030             var k = e.getKey();
37031             
37032             var c = e.getCharCode();
37033             
37034             if(
37035                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37036                     allowed.indexOf(String.fromCharCode(c)) === -1
37037             ){
37038                 e.stopEvent();
37039                 return;
37040             }
37041             
37042             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37043                 return;
37044             }
37045             
37046             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37047                 e.stopEvent();
37048             }
37049         };
37050         
37051         this.el.on("keypress", keyPress, this);
37052     },
37053     
37054     validateValue : function(value)
37055     {
37056         
37057         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37058             return false;
37059         }
37060         
37061         var num = this.parseValue(value);
37062         
37063         if(isNaN(num)){
37064             this.markInvalid(String.format(this.nanText, value));
37065             return false;
37066         }
37067         
37068         if(num < this.minValue){
37069             this.markInvalid(String.format(this.minText, this.minValue));
37070             return false;
37071         }
37072         
37073         if(num > this.maxValue){
37074             this.markInvalid(String.format(this.maxText, this.maxValue));
37075             return false;
37076         }
37077         
37078         return true;
37079     },
37080
37081     getValue : function()
37082     {
37083         var v = this.hiddenEl().getValue();
37084         
37085         return this.fixPrecision(this.parseValue(v));
37086     },
37087
37088     parseValue : function(value)
37089     {
37090         if(this.thousandsDelimiter) {
37091             value += "";
37092             r = new RegExp(",", "g");
37093             value = value.replace(r, "");
37094         }
37095         
37096         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37097         return isNaN(value) ? '' : value;
37098     },
37099
37100     fixPrecision : function(value)
37101     {
37102         if(this.thousandsDelimiter) {
37103             value += "";
37104             r = new RegExp(",", "g");
37105             value = value.replace(r, "");
37106         }
37107         
37108         var nan = isNaN(value);
37109         
37110         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37111             return nan ? '' : value;
37112         }
37113         return parseFloat(value).toFixed(this.decimalPrecision);
37114     },
37115
37116     setValue : function(v)
37117     {
37118         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37119         
37120         this.value = v;
37121         
37122         if(this.rendered){
37123             
37124             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37125             
37126             this.inputEl().dom.value = (v == '') ? '' :
37127                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37128             
37129             if(!this.allowZero && v === '0') {
37130                 this.hiddenEl().dom.value = '';
37131                 this.inputEl().dom.value = '';
37132             }
37133             
37134             this.validate();
37135         }
37136     },
37137
37138     decimalPrecisionFcn : function(v)
37139     {
37140         return Math.floor(v);
37141     },
37142
37143     beforeBlur : function()
37144     {
37145         var v = this.parseValue(this.getRawValue());
37146         
37147         if(v || v === 0 || v === ''){
37148             this.setValue(v);
37149         }
37150     },
37151     
37152     hiddenEl : function()
37153     {
37154         return this.el.select('input.hidden-number-input',true).first();
37155     }
37156     
37157 });
37158
37159  
37160
37161 /*
37162 * Licence: LGPL
37163 */
37164
37165 /**
37166  * @class Roo.bootstrap.DocumentSlider
37167  * @extends Roo.bootstrap.Component
37168  * Bootstrap DocumentSlider class
37169  * 
37170  * @constructor
37171  * Create a new DocumentViewer
37172  * @param {Object} config The config object
37173  */
37174
37175 Roo.bootstrap.DocumentSlider = function(config){
37176     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37177     
37178     this.files = [];
37179     
37180     this.addEvents({
37181         /**
37182          * @event initial
37183          * Fire after initEvent
37184          * @param {Roo.bootstrap.DocumentSlider} this
37185          */
37186         "initial" : true,
37187         /**
37188          * @event update
37189          * Fire after update
37190          * @param {Roo.bootstrap.DocumentSlider} this
37191          */
37192         "update" : true,
37193         /**
37194          * @event click
37195          * Fire after click
37196          * @param {Roo.bootstrap.DocumentSlider} this
37197          */
37198         "click" : true
37199     });
37200 };
37201
37202 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37203     
37204     files : false,
37205     
37206     indicator : 0,
37207     
37208     getAutoCreate : function()
37209     {
37210         var cfg = {
37211             tag : 'div',
37212             cls : 'roo-document-slider',
37213             cn : [
37214                 {
37215                     tag : 'div',
37216                     cls : 'roo-document-slider-header',
37217                     cn : [
37218                         {
37219                             tag : 'div',
37220                             cls : 'roo-document-slider-header-title'
37221                         }
37222                     ]
37223                 },
37224                 {
37225                     tag : 'div',
37226                     cls : 'roo-document-slider-body',
37227                     cn : [
37228                         {
37229                             tag : 'div',
37230                             cls : 'roo-document-slider-prev',
37231                             cn : [
37232                                 {
37233                                     tag : 'i',
37234                                     cls : 'fa fa-chevron-left'
37235                                 }
37236                             ]
37237                         },
37238                         {
37239                             tag : 'div',
37240                             cls : 'roo-document-slider-thumb',
37241                             cn : [
37242                                 {
37243                                     tag : 'img',
37244                                     cls : 'roo-document-slider-image'
37245                                 }
37246                             ]
37247                         },
37248                         {
37249                             tag : 'div',
37250                             cls : 'roo-document-slider-next',
37251                             cn : [
37252                                 {
37253                                     tag : 'i',
37254                                     cls : 'fa fa-chevron-right'
37255                                 }
37256                             ]
37257                         }
37258                     ]
37259                 }
37260             ]
37261         };
37262         
37263         return cfg;
37264     },
37265     
37266     initEvents : function()
37267     {
37268         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37269         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37270         
37271         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37272         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37273         
37274         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37275         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37276         
37277         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37278         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37279         
37280         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37281         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37282         
37283         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37284         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37285         
37286         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37287         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37288         
37289         this.thumbEl.on('click', this.onClick, this);
37290         
37291         this.prevIndicator.on('click', this.prev, this);
37292         
37293         this.nextIndicator.on('click', this.next, this);
37294         
37295     },
37296     
37297     initial : function()
37298     {
37299         if(this.files.length){
37300             this.indicator = 1;
37301             this.update()
37302         }
37303         
37304         this.fireEvent('initial', this);
37305     },
37306     
37307     update : function()
37308     {
37309         this.imageEl.attr('src', this.files[this.indicator - 1]);
37310         
37311         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37312         
37313         this.prevIndicator.show();
37314         
37315         if(this.indicator == 1){
37316             this.prevIndicator.hide();
37317         }
37318         
37319         this.nextIndicator.show();
37320         
37321         if(this.indicator == this.files.length){
37322             this.nextIndicator.hide();
37323         }
37324         
37325         this.thumbEl.scrollTo('top');
37326         
37327         this.fireEvent('update', this);
37328     },
37329     
37330     onClick : function(e)
37331     {
37332         e.preventDefault();
37333         
37334         this.fireEvent('click', this);
37335     },
37336     
37337     prev : function(e)
37338     {
37339         e.preventDefault();
37340         
37341         this.indicator = Math.max(1, this.indicator - 1);
37342         
37343         this.update();
37344     },
37345     
37346     next : function(e)
37347     {
37348         e.preventDefault();
37349         
37350         this.indicator = Math.min(this.files.length, this.indicator + 1);
37351         
37352         this.update();
37353     }
37354 });
37355 /*
37356  * - LGPL
37357  *
37358  * RadioSet
37359  *
37360  *
37361  */
37362
37363 /**
37364  * @class Roo.bootstrap.RadioSet
37365  * @extends Roo.bootstrap.Input
37366  * Bootstrap RadioSet class
37367  * @cfg {String} indicatorpos (left|right) default left
37368  * @cfg {Boolean} inline (true|false) inline the element (default true)
37369  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37370  * @constructor
37371  * Create a new RadioSet
37372  * @param {Object} config The config object
37373  */
37374
37375 Roo.bootstrap.RadioSet = function(config){
37376     
37377     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37378     
37379     this.radioes = [];
37380     
37381     Roo.bootstrap.RadioSet.register(this);
37382     
37383     this.addEvents({
37384         /**
37385         * @event check
37386         * Fires when the element is checked or unchecked.
37387         * @param {Roo.bootstrap.RadioSet} this This radio
37388         * @param {Roo.bootstrap.Radio} item The checked item
37389         */
37390        check : true,
37391        /**
37392         * @event click
37393         * Fires when the element is click.
37394         * @param {Roo.bootstrap.RadioSet} this This radio set
37395         * @param {Roo.bootstrap.Radio} item The checked item
37396         * @param {Roo.EventObject} e The event object
37397         */
37398        click : true
37399     });
37400     
37401 };
37402
37403 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37404
37405     radioes : false,
37406     
37407     inline : true,
37408     
37409     weight : '',
37410     
37411     indicatorpos : 'left',
37412     
37413     getAutoCreate : function()
37414     {
37415         var label = {
37416             tag : 'label',
37417             cls : 'roo-radio-set-label',
37418             cn : [
37419                 {
37420                     tag : 'span',
37421                     html : this.fieldLabel
37422                 }
37423             ]
37424         };
37425         if (Roo.bootstrap.version == 3) {
37426             
37427             
37428             if(this.indicatorpos == 'left'){
37429                 label.cn.unshift({
37430                     tag : 'i',
37431                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37432                     tooltip : 'This field is required'
37433                 });
37434             } else {
37435                 label.cn.push({
37436                     tag : 'i',
37437                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37438                     tooltip : 'This field is required'
37439                 });
37440             }
37441         }
37442         var items = {
37443             tag : 'div',
37444             cls : 'roo-radio-set-items'
37445         };
37446         
37447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37448         
37449         if (align === 'left' && this.fieldLabel.length) {
37450             
37451             items = {
37452                 cls : "roo-radio-set-right", 
37453                 cn: [
37454                     items
37455                 ]
37456             };
37457             
37458             if(this.labelWidth > 12){
37459                 label.style = "width: " + this.labelWidth + 'px';
37460             }
37461             
37462             if(this.labelWidth < 13 && this.labelmd == 0){
37463                 this.labelmd = this.labelWidth;
37464             }
37465             
37466             if(this.labellg > 0){
37467                 label.cls += ' col-lg-' + this.labellg;
37468                 items.cls += ' col-lg-' + (12 - this.labellg);
37469             }
37470             
37471             if(this.labelmd > 0){
37472                 label.cls += ' col-md-' + this.labelmd;
37473                 items.cls += ' col-md-' + (12 - this.labelmd);
37474             }
37475             
37476             if(this.labelsm > 0){
37477                 label.cls += ' col-sm-' + this.labelsm;
37478                 items.cls += ' col-sm-' + (12 - this.labelsm);
37479             }
37480             
37481             if(this.labelxs > 0){
37482                 label.cls += ' col-xs-' + this.labelxs;
37483                 items.cls += ' col-xs-' + (12 - this.labelxs);
37484             }
37485         }
37486         
37487         var cfg = {
37488             tag : 'div',
37489             cls : 'roo-radio-set',
37490             cn : [
37491                 {
37492                     tag : 'input',
37493                     cls : 'roo-radio-set-input',
37494                     type : 'hidden',
37495                     name : this.name,
37496                     value : this.value ? this.value :  ''
37497                 },
37498                 label,
37499                 items
37500             ]
37501         };
37502         
37503         if(this.weight.length){
37504             cfg.cls += ' roo-radio-' + this.weight;
37505         }
37506         
37507         if(this.inline) {
37508             cfg.cls += ' roo-radio-set-inline';
37509         }
37510         
37511         var settings=this;
37512         ['xs','sm','md','lg'].map(function(size){
37513             if (settings[size]) {
37514                 cfg.cls += ' col-' + size + '-' + settings[size];
37515             }
37516         });
37517         
37518         return cfg;
37519         
37520     },
37521
37522     initEvents : function()
37523     {
37524         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37525         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37526         
37527         if(!this.fieldLabel.length){
37528             this.labelEl.hide();
37529         }
37530         
37531         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37532         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37533         
37534         this.indicator = this.indicatorEl();
37535         
37536         if(this.indicator){
37537             this.indicator.addClass('invisible');
37538         }
37539         
37540         this.originalValue = this.getValue();
37541         
37542     },
37543     
37544     inputEl: function ()
37545     {
37546         return this.el.select('.roo-radio-set-input', true).first();
37547     },
37548     
37549     getChildContainer : function()
37550     {
37551         return this.itemsEl;
37552     },
37553     
37554     register : function(item)
37555     {
37556         this.radioes.push(item);
37557         
37558     },
37559     
37560     validate : function()
37561     {   
37562         if(this.getVisibilityEl().hasClass('hidden')){
37563             return true;
37564         }
37565         
37566         var valid = false;
37567         
37568         Roo.each(this.radioes, function(i){
37569             if(!i.checked){
37570                 return;
37571             }
37572             
37573             valid = true;
37574             return false;
37575         });
37576         
37577         if(this.allowBlank) {
37578             return true;
37579         }
37580         
37581         if(this.disabled || valid){
37582             this.markValid();
37583             return true;
37584         }
37585         
37586         this.markInvalid();
37587         return false;
37588         
37589     },
37590     
37591     markValid : function()
37592     {
37593         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37594             this.indicatorEl().removeClass('visible');
37595             this.indicatorEl().addClass('invisible');
37596         }
37597         
37598         
37599         if (Roo.bootstrap.version == 3) {
37600             this.el.removeClass([this.invalidClass, this.validClass]);
37601             this.el.addClass(this.validClass);
37602         } else {
37603             this.el.removeClass(['is-invalid','is-valid']);
37604             this.el.addClass(['is-valid']);
37605         }
37606         this.fireEvent('valid', this);
37607     },
37608     
37609     markInvalid : function(msg)
37610     {
37611         if(this.allowBlank || this.disabled){
37612             return;
37613         }
37614         
37615         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37616             this.indicatorEl().removeClass('invisible');
37617             this.indicatorEl().addClass('visible');
37618         }
37619         if (Roo.bootstrap.version == 3) {
37620             this.el.removeClass([this.invalidClass, this.validClass]);
37621             this.el.addClass(this.invalidClass);
37622         } else {
37623             this.el.removeClass(['is-invalid','is-valid']);
37624             this.el.addClass(['is-invalid']);
37625         }
37626         
37627         this.fireEvent('invalid', this, msg);
37628         
37629     },
37630     
37631     setValue : function(v, suppressEvent)
37632     {   
37633         if(this.value === v){
37634             return;
37635         }
37636         
37637         this.value = v;
37638         
37639         if(this.rendered){
37640             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37641         }
37642         
37643         Roo.each(this.radioes, function(i){
37644             i.checked = false;
37645             i.el.removeClass('checked');
37646         });
37647         
37648         Roo.each(this.radioes, function(i){
37649             
37650             if(i.value === v || i.value.toString() === v.toString()){
37651                 i.checked = true;
37652                 i.el.addClass('checked');
37653                 
37654                 if(suppressEvent !== true){
37655                     this.fireEvent('check', this, i);
37656                 }
37657                 
37658                 return false;
37659             }
37660             
37661         }, this);
37662         
37663         this.validate();
37664     },
37665     
37666     clearInvalid : function(){
37667         
37668         if(!this.el || this.preventMark){
37669             return;
37670         }
37671         
37672         this.el.removeClass([this.invalidClass]);
37673         
37674         this.fireEvent('valid', this);
37675     }
37676     
37677 });
37678
37679 Roo.apply(Roo.bootstrap.RadioSet, {
37680     
37681     groups: {},
37682     
37683     register : function(set)
37684     {
37685         this.groups[set.name] = set;
37686     },
37687     
37688     get: function(name) 
37689     {
37690         if (typeof(this.groups[name]) == 'undefined') {
37691             return false;
37692         }
37693         
37694         return this.groups[name] ;
37695     }
37696     
37697 });
37698 /*
37699  * Based on:
37700  * Ext JS Library 1.1.1
37701  * Copyright(c) 2006-2007, Ext JS, LLC.
37702  *
37703  * Originally Released Under LGPL - original licence link has changed is not relivant.
37704  *
37705  * Fork - LGPL
37706  * <script type="text/javascript">
37707  */
37708
37709
37710 /**
37711  * @class Roo.bootstrap.SplitBar
37712  * @extends Roo.util.Observable
37713  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37714  * <br><br>
37715  * Usage:
37716  * <pre><code>
37717 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37718                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37719 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37720 split.minSize = 100;
37721 split.maxSize = 600;
37722 split.animate = true;
37723 split.on('moved', splitterMoved);
37724 </code></pre>
37725  * @constructor
37726  * Create a new SplitBar
37727  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37728  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37729  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37730  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37731                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37732                         position of the SplitBar).
37733  */
37734 Roo.bootstrap.SplitBar = function(cfg){
37735     
37736     /** @private */
37737     
37738     //{
37739     //  dragElement : elm
37740     //  resizingElement: el,
37741         // optional..
37742     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37743     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37744         // existingProxy ???
37745     //}
37746     
37747     this.el = Roo.get(cfg.dragElement, true);
37748     this.el.dom.unselectable = "on";
37749     /** @private */
37750     this.resizingEl = Roo.get(cfg.resizingElement, true);
37751
37752     /**
37753      * @private
37754      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37755      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37756      * @type Number
37757      */
37758     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37759     
37760     /**
37761      * The minimum size of the resizing element. (Defaults to 0)
37762      * @type Number
37763      */
37764     this.minSize = 0;
37765     
37766     /**
37767      * The maximum size of the resizing element. (Defaults to 2000)
37768      * @type Number
37769      */
37770     this.maxSize = 2000;
37771     
37772     /**
37773      * Whether to animate the transition to the new size
37774      * @type Boolean
37775      */
37776     this.animate = false;
37777     
37778     /**
37779      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37780      * @type Boolean
37781      */
37782     this.useShim = false;
37783     
37784     /** @private */
37785     this.shim = null;
37786     
37787     if(!cfg.existingProxy){
37788         /** @private */
37789         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37790     }else{
37791         this.proxy = Roo.get(cfg.existingProxy).dom;
37792     }
37793     /** @private */
37794     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37795     
37796     /** @private */
37797     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37798     
37799     /** @private */
37800     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37801     
37802     /** @private */
37803     this.dragSpecs = {};
37804     
37805     /**
37806      * @private The adapter to use to positon and resize elements
37807      */
37808     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37809     this.adapter.init(this);
37810     
37811     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37812         /** @private */
37813         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37814         this.el.addClass("roo-splitbar-h");
37815     }else{
37816         /** @private */
37817         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37818         this.el.addClass("roo-splitbar-v");
37819     }
37820     
37821     this.addEvents({
37822         /**
37823          * @event resize
37824          * Fires when the splitter is moved (alias for {@link #event-moved})
37825          * @param {Roo.bootstrap.SplitBar} this
37826          * @param {Number} newSize the new width or height
37827          */
37828         "resize" : true,
37829         /**
37830          * @event moved
37831          * Fires when the splitter is moved
37832          * @param {Roo.bootstrap.SplitBar} this
37833          * @param {Number} newSize the new width or height
37834          */
37835         "moved" : true,
37836         /**
37837          * @event beforeresize
37838          * Fires before the splitter is dragged
37839          * @param {Roo.bootstrap.SplitBar} this
37840          */
37841         "beforeresize" : true,
37842
37843         "beforeapply" : true
37844     });
37845
37846     Roo.util.Observable.call(this);
37847 };
37848
37849 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37850     onStartProxyDrag : function(x, y){
37851         this.fireEvent("beforeresize", this);
37852         if(!this.overlay){
37853             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37854             o.unselectable();
37855             o.enableDisplayMode("block");
37856             // all splitbars share the same overlay
37857             Roo.bootstrap.SplitBar.prototype.overlay = o;
37858         }
37859         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37860         this.overlay.show();
37861         Roo.get(this.proxy).setDisplayed("block");
37862         var size = this.adapter.getElementSize(this);
37863         this.activeMinSize = this.getMinimumSize();;
37864         this.activeMaxSize = this.getMaximumSize();;
37865         var c1 = size - this.activeMinSize;
37866         var c2 = Math.max(this.activeMaxSize - size, 0);
37867         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37868             this.dd.resetConstraints();
37869             this.dd.setXConstraint(
37870                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37871                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37872             );
37873             this.dd.setYConstraint(0, 0);
37874         }else{
37875             this.dd.resetConstraints();
37876             this.dd.setXConstraint(0, 0);
37877             this.dd.setYConstraint(
37878                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37879                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37880             );
37881          }
37882         this.dragSpecs.startSize = size;
37883         this.dragSpecs.startPoint = [x, y];
37884         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37885     },
37886     
37887     /** 
37888      * @private Called after the drag operation by the DDProxy
37889      */
37890     onEndProxyDrag : function(e){
37891         Roo.get(this.proxy).setDisplayed(false);
37892         var endPoint = Roo.lib.Event.getXY(e);
37893         if(this.overlay){
37894             this.overlay.hide();
37895         }
37896         var newSize;
37897         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37898             newSize = this.dragSpecs.startSize + 
37899                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37900                     endPoint[0] - this.dragSpecs.startPoint[0] :
37901                     this.dragSpecs.startPoint[0] - endPoint[0]
37902                 );
37903         }else{
37904             newSize = this.dragSpecs.startSize + 
37905                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37906                     endPoint[1] - this.dragSpecs.startPoint[1] :
37907                     this.dragSpecs.startPoint[1] - endPoint[1]
37908                 );
37909         }
37910         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37911         if(newSize != this.dragSpecs.startSize){
37912             if(this.fireEvent('beforeapply', this, newSize) !== false){
37913                 this.adapter.setElementSize(this, newSize);
37914                 this.fireEvent("moved", this, newSize);
37915                 this.fireEvent("resize", this, newSize);
37916             }
37917         }
37918     },
37919     
37920     /**
37921      * Get the adapter this SplitBar uses
37922      * @return The adapter object
37923      */
37924     getAdapter : function(){
37925         return this.adapter;
37926     },
37927     
37928     /**
37929      * Set the adapter this SplitBar uses
37930      * @param {Object} adapter A SplitBar adapter object
37931      */
37932     setAdapter : function(adapter){
37933         this.adapter = adapter;
37934         this.adapter.init(this);
37935     },
37936     
37937     /**
37938      * Gets the minimum size for the resizing element
37939      * @return {Number} The minimum size
37940      */
37941     getMinimumSize : function(){
37942         return this.minSize;
37943     },
37944     
37945     /**
37946      * Sets the minimum size for the resizing element
37947      * @param {Number} minSize The minimum size
37948      */
37949     setMinimumSize : function(minSize){
37950         this.minSize = minSize;
37951     },
37952     
37953     /**
37954      * Gets the maximum size for the resizing element
37955      * @return {Number} The maximum size
37956      */
37957     getMaximumSize : function(){
37958         return this.maxSize;
37959     },
37960     
37961     /**
37962      * Sets the maximum size for the resizing element
37963      * @param {Number} maxSize The maximum size
37964      */
37965     setMaximumSize : function(maxSize){
37966         this.maxSize = maxSize;
37967     },
37968     
37969     /**
37970      * Sets the initialize size for the resizing element
37971      * @param {Number} size The initial size
37972      */
37973     setCurrentSize : function(size){
37974         var oldAnimate = this.animate;
37975         this.animate = false;
37976         this.adapter.setElementSize(this, size);
37977         this.animate = oldAnimate;
37978     },
37979     
37980     /**
37981      * Destroy this splitbar. 
37982      * @param {Boolean} removeEl True to remove the element
37983      */
37984     destroy : function(removeEl){
37985         if(this.shim){
37986             this.shim.remove();
37987         }
37988         this.dd.unreg();
37989         this.proxy.parentNode.removeChild(this.proxy);
37990         if(removeEl){
37991             this.el.remove();
37992         }
37993     }
37994 });
37995
37996 /**
37997  * @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.
37998  */
37999 Roo.bootstrap.SplitBar.createProxy = function(dir){
38000     var proxy = new Roo.Element(document.createElement("div"));
38001     proxy.unselectable();
38002     var cls = 'roo-splitbar-proxy';
38003     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38004     document.body.appendChild(proxy.dom);
38005     return proxy.dom;
38006 };
38007
38008 /** 
38009  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38010  * Default Adapter. It assumes the splitter and resizing element are not positioned
38011  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38012  */
38013 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38014 };
38015
38016 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38017     // do nothing for now
38018     init : function(s){
38019     
38020     },
38021     /**
38022      * Called before drag operations to get the current size of the resizing element. 
38023      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38024      */
38025      getElementSize : function(s){
38026         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38027             return s.resizingEl.getWidth();
38028         }else{
38029             return s.resizingEl.getHeight();
38030         }
38031     },
38032     
38033     /**
38034      * Called after drag operations to set the size of the resizing element.
38035      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38036      * @param {Number} newSize The new size to set
38037      * @param {Function} onComplete A function to be invoked when resizing is complete
38038      */
38039     setElementSize : function(s, newSize, onComplete){
38040         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38041             if(!s.animate){
38042                 s.resizingEl.setWidth(newSize);
38043                 if(onComplete){
38044                     onComplete(s, newSize);
38045                 }
38046             }else{
38047                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38048             }
38049         }else{
38050             
38051             if(!s.animate){
38052                 s.resizingEl.setHeight(newSize);
38053                 if(onComplete){
38054                     onComplete(s, newSize);
38055                 }
38056             }else{
38057                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38058             }
38059         }
38060     }
38061 };
38062
38063 /** 
38064  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38065  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38066  * Adapter that  moves the splitter element to align with the resized sizing element. 
38067  * Used with an absolute positioned SplitBar.
38068  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38069  * document.body, make sure you assign an id to the body element.
38070  */
38071 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38072     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38073     this.container = Roo.get(container);
38074 };
38075
38076 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38077     init : function(s){
38078         this.basic.init(s);
38079     },
38080     
38081     getElementSize : function(s){
38082         return this.basic.getElementSize(s);
38083     },
38084     
38085     setElementSize : function(s, newSize, onComplete){
38086         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38087     },
38088     
38089     moveSplitter : function(s){
38090         var yes = Roo.bootstrap.SplitBar;
38091         switch(s.placement){
38092             case yes.LEFT:
38093                 s.el.setX(s.resizingEl.getRight());
38094                 break;
38095             case yes.RIGHT:
38096                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38097                 break;
38098             case yes.TOP:
38099                 s.el.setY(s.resizingEl.getBottom());
38100                 break;
38101             case yes.BOTTOM:
38102                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38103                 break;
38104         }
38105     }
38106 };
38107
38108 /**
38109  * Orientation constant - Create a vertical SplitBar
38110  * @static
38111  * @type Number
38112  */
38113 Roo.bootstrap.SplitBar.VERTICAL = 1;
38114
38115 /**
38116  * Orientation constant - Create a horizontal SplitBar
38117  * @static
38118  * @type Number
38119  */
38120 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38121
38122 /**
38123  * Placement constant - The resizing element is to the left of the splitter element
38124  * @static
38125  * @type Number
38126  */
38127 Roo.bootstrap.SplitBar.LEFT = 1;
38128
38129 /**
38130  * Placement constant - The resizing element is to the right of the splitter element
38131  * @static
38132  * @type Number
38133  */
38134 Roo.bootstrap.SplitBar.RIGHT = 2;
38135
38136 /**
38137  * Placement constant - The resizing element is positioned above the splitter element
38138  * @static
38139  * @type Number
38140  */
38141 Roo.bootstrap.SplitBar.TOP = 3;
38142
38143 /**
38144  * Placement constant - The resizing element is positioned under splitter element
38145  * @static
38146  * @type Number
38147  */
38148 Roo.bootstrap.SplitBar.BOTTOM = 4;
38149 Roo.namespace("Roo.bootstrap.layout");/*
38150  * Based on:
38151  * Ext JS Library 1.1.1
38152  * Copyright(c) 2006-2007, Ext JS, LLC.
38153  *
38154  * Originally Released Under LGPL - original licence link has changed is not relivant.
38155  *
38156  * Fork - LGPL
38157  * <script type="text/javascript">
38158  */
38159
38160 /**
38161  * @class Roo.bootstrap.layout.Manager
38162  * @extends Roo.bootstrap.Component
38163  * Base class for layout managers.
38164  */
38165 Roo.bootstrap.layout.Manager = function(config)
38166 {
38167     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38168
38169
38170
38171
38172
38173     /** false to disable window resize monitoring @type Boolean */
38174     this.monitorWindowResize = true;
38175     this.regions = {};
38176     this.addEvents({
38177         /**
38178          * @event layout
38179          * Fires when a layout is performed.
38180          * @param {Roo.LayoutManager} this
38181          */
38182         "layout" : true,
38183         /**
38184          * @event regionresized
38185          * Fires when the user resizes a region.
38186          * @param {Roo.LayoutRegion} region The resized region
38187          * @param {Number} newSize The new size (width for east/west, height for north/south)
38188          */
38189         "regionresized" : true,
38190         /**
38191          * @event regioncollapsed
38192          * Fires when a region is collapsed.
38193          * @param {Roo.LayoutRegion} region The collapsed region
38194          */
38195         "regioncollapsed" : true,
38196         /**
38197          * @event regionexpanded
38198          * Fires when a region is expanded.
38199          * @param {Roo.LayoutRegion} region The expanded region
38200          */
38201         "regionexpanded" : true
38202     });
38203     this.updating = false;
38204
38205     if (config.el) {
38206         this.el = Roo.get(config.el);
38207         this.initEvents();
38208     }
38209
38210 };
38211
38212 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38213
38214
38215     regions : null,
38216
38217     monitorWindowResize : true,
38218
38219
38220     updating : false,
38221
38222
38223     onRender : function(ct, position)
38224     {
38225         if(!this.el){
38226             this.el = Roo.get(ct);
38227             this.initEvents();
38228         }
38229         //this.fireEvent('render',this);
38230     },
38231
38232
38233     initEvents: function()
38234     {
38235
38236
38237         // ie scrollbar fix
38238         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38239             document.body.scroll = "no";
38240         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38241             this.el.position('relative');
38242         }
38243         this.id = this.el.id;
38244         this.el.addClass("roo-layout-container");
38245         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38246         if(this.el.dom != document.body ) {
38247             this.el.on('resize', this.layout,this);
38248             this.el.on('show', this.layout,this);
38249         }
38250
38251     },
38252
38253     /**
38254      * Returns true if this layout is currently being updated
38255      * @return {Boolean}
38256      */
38257     isUpdating : function(){
38258         return this.updating;
38259     },
38260
38261     /**
38262      * Suspend the LayoutManager from doing auto-layouts while
38263      * making multiple add or remove calls
38264      */
38265     beginUpdate : function(){
38266         this.updating = true;
38267     },
38268
38269     /**
38270      * Restore auto-layouts and optionally disable the manager from performing a layout
38271      * @param {Boolean} noLayout true to disable a layout update
38272      */
38273     endUpdate : function(noLayout){
38274         this.updating = false;
38275         if(!noLayout){
38276             this.layout();
38277         }
38278     },
38279
38280     layout: function(){
38281         // abstract...
38282     },
38283
38284     onRegionResized : function(region, newSize){
38285         this.fireEvent("regionresized", region, newSize);
38286         this.layout();
38287     },
38288
38289     onRegionCollapsed : function(region){
38290         this.fireEvent("regioncollapsed", region);
38291     },
38292
38293     onRegionExpanded : function(region){
38294         this.fireEvent("regionexpanded", region);
38295     },
38296
38297     /**
38298      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38299      * performs box-model adjustments.
38300      * @return {Object} The size as an object {width: (the width), height: (the height)}
38301      */
38302     getViewSize : function()
38303     {
38304         var size;
38305         if(this.el.dom != document.body){
38306             size = this.el.getSize();
38307         }else{
38308             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38309         }
38310         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38311         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38312         return size;
38313     },
38314
38315     /**
38316      * Returns the Element this layout is bound to.
38317      * @return {Roo.Element}
38318      */
38319     getEl : function(){
38320         return this.el;
38321     },
38322
38323     /**
38324      * Returns the specified region.
38325      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38326      * @return {Roo.LayoutRegion}
38327      */
38328     getRegion : function(target){
38329         return this.regions[target.toLowerCase()];
38330     },
38331
38332     onWindowResize : function(){
38333         if(this.monitorWindowResize){
38334             this.layout();
38335         }
38336     }
38337 });
38338 /*
38339  * Based on:
38340  * Ext JS Library 1.1.1
38341  * Copyright(c) 2006-2007, Ext JS, LLC.
38342  *
38343  * Originally Released Under LGPL - original licence link has changed is not relivant.
38344  *
38345  * Fork - LGPL
38346  * <script type="text/javascript">
38347  */
38348 /**
38349  * @class Roo.bootstrap.layout.Border
38350  * @extends Roo.bootstrap.layout.Manager
38351  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38352  * please see: examples/bootstrap/nested.html<br><br>
38353  
38354 <b>The container the layout is rendered into can be either the body element or any other element.
38355 If it is not the body element, the container needs to either be an absolute positioned element,
38356 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38357 the container size if it is not the body element.</b>
38358
38359 * @constructor
38360 * Create a new Border
38361 * @param {Object} config Configuration options
38362  */
38363 Roo.bootstrap.layout.Border = function(config){
38364     config = config || {};
38365     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38366     
38367     
38368     
38369     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38370         if(config[region]){
38371             config[region].region = region;
38372             this.addRegion(config[region]);
38373         }
38374     },this);
38375     
38376 };
38377
38378 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38379
38380 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38381     
38382     parent : false, // this might point to a 'nest' or a ???
38383     
38384     /**
38385      * Creates and adds a new region if it doesn't already exist.
38386      * @param {String} target The target region key (north, south, east, west or center).
38387      * @param {Object} config The regions config object
38388      * @return {BorderLayoutRegion} The new region
38389      */
38390     addRegion : function(config)
38391     {
38392         if(!this.regions[config.region]){
38393             var r = this.factory(config);
38394             this.bindRegion(r);
38395         }
38396         return this.regions[config.region];
38397     },
38398
38399     // private (kinda)
38400     bindRegion : function(r){
38401         this.regions[r.config.region] = r;
38402         
38403         r.on("visibilitychange",    this.layout, this);
38404         r.on("paneladded",          this.layout, this);
38405         r.on("panelremoved",        this.layout, this);
38406         r.on("invalidated",         this.layout, this);
38407         r.on("resized",             this.onRegionResized, this);
38408         r.on("collapsed",           this.onRegionCollapsed, this);
38409         r.on("expanded",            this.onRegionExpanded, this);
38410     },
38411
38412     /**
38413      * Performs a layout update.
38414      */
38415     layout : function()
38416     {
38417         if(this.updating) {
38418             return;
38419         }
38420         
38421         // render all the rebions if they have not been done alreayd?
38422         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38423             if(this.regions[region] && !this.regions[region].bodyEl){
38424                 this.regions[region].onRender(this.el)
38425             }
38426         },this);
38427         
38428         var size = this.getViewSize();
38429         var w = size.width;
38430         var h = size.height;
38431         var centerW = w;
38432         var centerH = h;
38433         var centerY = 0;
38434         var centerX = 0;
38435         //var x = 0, y = 0;
38436
38437         var rs = this.regions;
38438         var north = rs["north"];
38439         var south = rs["south"]; 
38440         var west = rs["west"];
38441         var east = rs["east"];
38442         var center = rs["center"];
38443         //if(this.hideOnLayout){ // not supported anymore
38444             //c.el.setStyle("display", "none");
38445         //}
38446         if(north && north.isVisible()){
38447             var b = north.getBox();
38448             var m = north.getMargins();
38449             b.width = w - (m.left+m.right);
38450             b.x = m.left;
38451             b.y = m.top;
38452             centerY = b.height + b.y + m.bottom;
38453             centerH -= centerY;
38454             north.updateBox(this.safeBox(b));
38455         }
38456         if(south && south.isVisible()){
38457             var b = south.getBox();
38458             var m = south.getMargins();
38459             b.width = w - (m.left+m.right);
38460             b.x = m.left;
38461             var totalHeight = (b.height + m.top + m.bottom);
38462             b.y = h - totalHeight + m.top;
38463             centerH -= totalHeight;
38464             south.updateBox(this.safeBox(b));
38465         }
38466         if(west && west.isVisible()){
38467             var b = west.getBox();
38468             var m = west.getMargins();
38469             b.height = centerH - (m.top+m.bottom);
38470             b.x = m.left;
38471             b.y = centerY + m.top;
38472             var totalWidth = (b.width + m.left + m.right);
38473             centerX += totalWidth;
38474             centerW -= totalWidth;
38475             west.updateBox(this.safeBox(b));
38476         }
38477         if(east && east.isVisible()){
38478             var b = east.getBox();
38479             var m = east.getMargins();
38480             b.height = centerH - (m.top+m.bottom);
38481             var totalWidth = (b.width + m.left + m.right);
38482             b.x = w - totalWidth + m.left;
38483             b.y = centerY + m.top;
38484             centerW -= totalWidth;
38485             east.updateBox(this.safeBox(b));
38486         }
38487         if(center){
38488             var m = center.getMargins();
38489             var centerBox = {
38490                 x: centerX + m.left,
38491                 y: centerY + m.top,
38492                 width: centerW - (m.left+m.right),
38493                 height: centerH - (m.top+m.bottom)
38494             };
38495             //if(this.hideOnLayout){
38496                 //center.el.setStyle("display", "block");
38497             //}
38498             center.updateBox(this.safeBox(centerBox));
38499         }
38500         this.el.repaint();
38501         this.fireEvent("layout", this);
38502     },
38503
38504     // private
38505     safeBox : function(box){
38506         box.width = Math.max(0, box.width);
38507         box.height = Math.max(0, box.height);
38508         return box;
38509     },
38510
38511     /**
38512      * Adds a ContentPanel (or subclass) to this layout.
38513      * @param {String} target The target region key (north, south, east, west or center).
38514      * @param {Roo.ContentPanel} panel The panel to add
38515      * @return {Roo.ContentPanel} The added panel
38516      */
38517     add : function(target, panel){
38518          
38519         target = target.toLowerCase();
38520         return this.regions[target].add(panel);
38521     },
38522
38523     /**
38524      * Remove a ContentPanel (or subclass) to this layout.
38525      * @param {String} target The target region key (north, south, east, west or center).
38526      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38527      * @return {Roo.ContentPanel} The removed panel
38528      */
38529     remove : function(target, panel){
38530         target = target.toLowerCase();
38531         return this.regions[target].remove(panel);
38532     },
38533
38534     /**
38535      * Searches all regions for a panel with the specified id
38536      * @param {String} panelId
38537      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38538      */
38539     findPanel : function(panelId){
38540         var rs = this.regions;
38541         for(var target in rs){
38542             if(typeof rs[target] != "function"){
38543                 var p = rs[target].getPanel(panelId);
38544                 if(p){
38545                     return p;
38546                 }
38547             }
38548         }
38549         return null;
38550     },
38551
38552     /**
38553      * Searches all regions for a panel with the specified id and activates (shows) it.
38554      * @param {String/ContentPanel} panelId The panels id or the panel itself
38555      * @return {Roo.ContentPanel} The shown panel or null
38556      */
38557     showPanel : function(panelId) {
38558       var rs = this.regions;
38559       for(var target in rs){
38560          var r = rs[target];
38561          if(typeof r != "function"){
38562             if(r.hasPanel(panelId)){
38563                return r.showPanel(panelId);
38564             }
38565          }
38566       }
38567       return null;
38568    },
38569
38570    /**
38571      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38572      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38573      */
38574    /*
38575     restoreState : function(provider){
38576         if(!provider){
38577             provider = Roo.state.Manager;
38578         }
38579         var sm = new Roo.LayoutStateManager();
38580         sm.init(this, provider);
38581     },
38582 */
38583  
38584  
38585     /**
38586      * Adds a xtype elements to the layout.
38587      * <pre><code>
38588
38589 layout.addxtype({
38590        xtype : 'ContentPanel',
38591        region: 'west',
38592        items: [ .... ]
38593    }
38594 );
38595
38596 layout.addxtype({
38597         xtype : 'NestedLayoutPanel',
38598         region: 'west',
38599         layout: {
38600            center: { },
38601            west: { }   
38602         },
38603         items : [ ... list of content panels or nested layout panels.. ]
38604    }
38605 );
38606 </code></pre>
38607      * @param {Object} cfg Xtype definition of item to add.
38608      */
38609     addxtype : function(cfg)
38610     {
38611         // basically accepts a pannel...
38612         // can accept a layout region..!?!?
38613         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38614         
38615         
38616         // theory?  children can only be panels??
38617         
38618         //if (!cfg.xtype.match(/Panel$/)) {
38619         //    return false;
38620         //}
38621         var ret = false;
38622         
38623         if (typeof(cfg.region) == 'undefined') {
38624             Roo.log("Failed to add Panel, region was not set");
38625             Roo.log(cfg);
38626             return false;
38627         }
38628         var region = cfg.region;
38629         delete cfg.region;
38630         
38631           
38632         var xitems = [];
38633         if (cfg.items) {
38634             xitems = cfg.items;
38635             delete cfg.items;
38636         }
38637         var nb = false;
38638         
38639         if ( region == 'center') {
38640             Roo.log("Center: " + cfg.title);
38641         }
38642         
38643         
38644         switch(cfg.xtype) 
38645         {
38646             case 'Content':  // ContentPanel (el, cfg)
38647             case 'Scroll':  // ContentPanel (el, cfg)
38648             case 'View': 
38649                 cfg.autoCreate = cfg.autoCreate || true;
38650                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38651                 //} else {
38652                 //    var el = this.el.createChild();
38653                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38654                 //}
38655                 
38656                 this.add(region, ret);
38657                 break;
38658             
38659             /*
38660             case 'TreePanel': // our new panel!
38661                 cfg.el = this.el.createChild();
38662                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38663                 this.add(region, ret);
38664                 break;
38665             */
38666             
38667             case 'Nest': 
38668                 // create a new Layout (which is  a Border Layout...
38669                 
38670                 var clayout = cfg.layout;
38671                 clayout.el  = this.el.createChild();
38672                 clayout.items   = clayout.items  || [];
38673                 
38674                 delete cfg.layout;
38675                 
38676                 // replace this exitems with the clayout ones..
38677                 xitems = clayout.items;
38678                  
38679                 // force background off if it's in center...
38680                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38681                     cfg.background = false;
38682                 }
38683                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38684                 
38685                 
38686                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38687                 //console.log('adding nested layout panel '  + cfg.toSource());
38688                 this.add(region, ret);
38689                 nb = {}; /// find first...
38690                 break;
38691             
38692             case 'Grid':
38693                 
38694                 // needs grid and region
38695                 
38696                 //var el = this.getRegion(region).el.createChild();
38697                 /*
38698                  *var el = this.el.createChild();
38699                 // create the grid first...
38700                 cfg.grid.container = el;
38701                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38702                 */
38703                 
38704                 if (region == 'center' && this.active ) {
38705                     cfg.background = false;
38706                 }
38707                 
38708                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38709                 
38710                 this.add(region, ret);
38711                 /*
38712                 if (cfg.background) {
38713                     // render grid on panel activation (if panel background)
38714                     ret.on('activate', function(gp) {
38715                         if (!gp.grid.rendered) {
38716                     //        gp.grid.render(el);
38717                         }
38718                     });
38719                 } else {
38720                   //  cfg.grid.render(el);
38721                 }
38722                 */
38723                 break;
38724            
38725            
38726             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38727                 // it was the old xcomponent building that caused this before.
38728                 // espeically if border is the top element in the tree.
38729                 ret = this;
38730                 break; 
38731                 
38732                     
38733                 
38734                 
38735                 
38736             default:
38737                 /*
38738                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38739                     
38740                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38741                     this.add(region, ret);
38742                 } else {
38743                 */
38744                     Roo.log(cfg);
38745                     throw "Can not add '" + cfg.xtype + "' to Border";
38746                     return null;
38747              
38748                                 
38749              
38750         }
38751         this.beginUpdate();
38752         // add children..
38753         var region = '';
38754         var abn = {};
38755         Roo.each(xitems, function(i)  {
38756             region = nb && i.region ? i.region : false;
38757             
38758             var add = ret.addxtype(i);
38759            
38760             if (region) {
38761                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38762                 if (!i.background) {
38763                     abn[region] = nb[region] ;
38764                 }
38765             }
38766             
38767         });
38768         this.endUpdate();
38769
38770         // make the last non-background panel active..
38771         //if (nb) { Roo.log(abn); }
38772         if (nb) {
38773             
38774             for(var r in abn) {
38775                 region = this.getRegion(r);
38776                 if (region) {
38777                     // tried using nb[r], but it does not work..
38778                      
38779                     region.showPanel(abn[r]);
38780                    
38781                 }
38782             }
38783         }
38784         return ret;
38785         
38786     },
38787     
38788     
38789 // private
38790     factory : function(cfg)
38791     {
38792         
38793         var validRegions = Roo.bootstrap.layout.Border.regions;
38794
38795         var target = cfg.region;
38796         cfg.mgr = this;
38797         
38798         var r = Roo.bootstrap.layout;
38799         Roo.log(target);
38800         switch(target){
38801             case "north":
38802                 return new r.North(cfg);
38803             case "south":
38804                 return new r.South(cfg);
38805             case "east":
38806                 return new r.East(cfg);
38807             case "west":
38808                 return new r.West(cfg);
38809             case "center":
38810                 return new r.Center(cfg);
38811         }
38812         throw 'Layout region "'+target+'" not supported.';
38813     }
38814     
38815     
38816 });
38817  /*
38818  * Based on:
38819  * Ext JS Library 1.1.1
38820  * Copyright(c) 2006-2007, Ext JS, LLC.
38821  *
38822  * Originally Released Under LGPL - original licence link has changed is not relivant.
38823  *
38824  * Fork - LGPL
38825  * <script type="text/javascript">
38826  */
38827  
38828 /**
38829  * @class Roo.bootstrap.layout.Basic
38830  * @extends Roo.util.Observable
38831  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38832  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38833  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38834  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38835  * @cfg {string}   region  the region that it inhabits..
38836  * @cfg {bool}   skipConfig skip config?
38837  * 
38838
38839  */
38840 Roo.bootstrap.layout.Basic = function(config){
38841     
38842     this.mgr = config.mgr;
38843     
38844     this.position = config.region;
38845     
38846     var skipConfig = config.skipConfig;
38847     
38848     this.events = {
38849         /**
38850          * @scope Roo.BasicLayoutRegion
38851          */
38852         
38853         /**
38854          * @event beforeremove
38855          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38856          * @param {Roo.LayoutRegion} this
38857          * @param {Roo.ContentPanel} panel The panel
38858          * @param {Object} e The cancel event object
38859          */
38860         "beforeremove" : true,
38861         /**
38862          * @event invalidated
38863          * Fires when the layout for this region is changed.
38864          * @param {Roo.LayoutRegion} this
38865          */
38866         "invalidated" : true,
38867         /**
38868          * @event visibilitychange
38869          * Fires when this region is shown or hidden 
38870          * @param {Roo.LayoutRegion} this
38871          * @param {Boolean} visibility true or false
38872          */
38873         "visibilitychange" : true,
38874         /**
38875          * @event paneladded
38876          * Fires when a panel is added. 
38877          * @param {Roo.LayoutRegion} this
38878          * @param {Roo.ContentPanel} panel The panel
38879          */
38880         "paneladded" : true,
38881         /**
38882          * @event panelremoved
38883          * Fires when a panel is removed. 
38884          * @param {Roo.LayoutRegion} this
38885          * @param {Roo.ContentPanel} panel The panel
38886          */
38887         "panelremoved" : true,
38888         /**
38889          * @event beforecollapse
38890          * Fires when this region before collapse.
38891          * @param {Roo.LayoutRegion} this
38892          */
38893         "beforecollapse" : true,
38894         /**
38895          * @event collapsed
38896          * Fires when this region is collapsed.
38897          * @param {Roo.LayoutRegion} this
38898          */
38899         "collapsed" : true,
38900         /**
38901          * @event expanded
38902          * Fires when this region is expanded.
38903          * @param {Roo.LayoutRegion} this
38904          */
38905         "expanded" : true,
38906         /**
38907          * @event slideshow
38908          * Fires when this region is slid into view.
38909          * @param {Roo.LayoutRegion} this
38910          */
38911         "slideshow" : true,
38912         /**
38913          * @event slidehide
38914          * Fires when this region slides out of view. 
38915          * @param {Roo.LayoutRegion} this
38916          */
38917         "slidehide" : true,
38918         /**
38919          * @event panelactivated
38920          * Fires when a panel is activated. 
38921          * @param {Roo.LayoutRegion} this
38922          * @param {Roo.ContentPanel} panel The activated panel
38923          */
38924         "panelactivated" : true,
38925         /**
38926          * @event resized
38927          * Fires when the user resizes this region. 
38928          * @param {Roo.LayoutRegion} this
38929          * @param {Number} newSize The new size (width for east/west, height for north/south)
38930          */
38931         "resized" : true
38932     };
38933     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38934     this.panels = new Roo.util.MixedCollection();
38935     this.panels.getKey = this.getPanelId.createDelegate(this);
38936     this.box = null;
38937     this.activePanel = null;
38938     // ensure listeners are added...
38939     
38940     if (config.listeners || config.events) {
38941         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38942             listeners : config.listeners || {},
38943             events : config.events || {}
38944         });
38945     }
38946     
38947     if(skipConfig !== true){
38948         this.applyConfig(config);
38949     }
38950 };
38951
38952 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38953 {
38954     getPanelId : function(p){
38955         return p.getId();
38956     },
38957     
38958     applyConfig : function(config){
38959         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38960         this.config = config;
38961         
38962     },
38963     
38964     /**
38965      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38966      * the width, for horizontal (north, south) the height.
38967      * @param {Number} newSize The new width or height
38968      */
38969     resizeTo : function(newSize){
38970         var el = this.el ? this.el :
38971                  (this.activePanel ? this.activePanel.getEl() : null);
38972         if(el){
38973             switch(this.position){
38974                 case "east":
38975                 case "west":
38976                     el.setWidth(newSize);
38977                     this.fireEvent("resized", this, newSize);
38978                 break;
38979                 case "north":
38980                 case "south":
38981                     el.setHeight(newSize);
38982                     this.fireEvent("resized", this, newSize);
38983                 break;                
38984             }
38985         }
38986     },
38987     
38988     getBox : function(){
38989         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38990     },
38991     
38992     getMargins : function(){
38993         return this.margins;
38994     },
38995     
38996     updateBox : function(box){
38997         this.box = box;
38998         var el = this.activePanel.getEl();
38999         el.dom.style.left = box.x + "px";
39000         el.dom.style.top = box.y + "px";
39001         this.activePanel.setSize(box.width, box.height);
39002     },
39003     
39004     /**
39005      * Returns the container element for this region.
39006      * @return {Roo.Element}
39007      */
39008     getEl : function(){
39009         return this.activePanel;
39010     },
39011     
39012     /**
39013      * Returns true if this region is currently visible.
39014      * @return {Boolean}
39015      */
39016     isVisible : function(){
39017         return this.activePanel ? true : false;
39018     },
39019     
39020     setActivePanel : function(panel){
39021         panel = this.getPanel(panel);
39022         if(this.activePanel && this.activePanel != panel){
39023             this.activePanel.setActiveState(false);
39024             this.activePanel.getEl().setLeftTop(-10000,-10000);
39025         }
39026         this.activePanel = panel;
39027         panel.setActiveState(true);
39028         if(this.box){
39029             panel.setSize(this.box.width, this.box.height);
39030         }
39031         this.fireEvent("panelactivated", this, panel);
39032         this.fireEvent("invalidated");
39033     },
39034     
39035     /**
39036      * Show the specified panel.
39037      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39038      * @return {Roo.ContentPanel} The shown panel or null
39039      */
39040     showPanel : function(panel){
39041         panel = this.getPanel(panel);
39042         if(panel){
39043             this.setActivePanel(panel);
39044         }
39045         return panel;
39046     },
39047     
39048     /**
39049      * Get the active panel for this region.
39050      * @return {Roo.ContentPanel} The active panel or null
39051      */
39052     getActivePanel : function(){
39053         return this.activePanel;
39054     },
39055     
39056     /**
39057      * Add the passed ContentPanel(s)
39058      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39059      * @return {Roo.ContentPanel} The panel added (if only one was added)
39060      */
39061     add : function(panel){
39062         if(arguments.length > 1){
39063             for(var i = 0, len = arguments.length; i < len; i++) {
39064                 this.add(arguments[i]);
39065             }
39066             return null;
39067         }
39068         if(this.hasPanel(panel)){
39069             this.showPanel(panel);
39070             return panel;
39071         }
39072         var el = panel.getEl();
39073         if(el.dom.parentNode != this.mgr.el.dom){
39074             this.mgr.el.dom.appendChild(el.dom);
39075         }
39076         if(panel.setRegion){
39077             panel.setRegion(this);
39078         }
39079         this.panels.add(panel);
39080         el.setStyle("position", "absolute");
39081         if(!panel.background){
39082             this.setActivePanel(panel);
39083             if(this.config.initialSize && this.panels.getCount()==1){
39084                 this.resizeTo(this.config.initialSize);
39085             }
39086         }
39087         this.fireEvent("paneladded", this, panel);
39088         return panel;
39089     },
39090     
39091     /**
39092      * Returns true if the panel is in this region.
39093      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39094      * @return {Boolean}
39095      */
39096     hasPanel : function(panel){
39097         if(typeof panel == "object"){ // must be panel obj
39098             panel = panel.getId();
39099         }
39100         return this.getPanel(panel) ? true : false;
39101     },
39102     
39103     /**
39104      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39105      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39106      * @param {Boolean} preservePanel Overrides the config preservePanel option
39107      * @return {Roo.ContentPanel} The panel that was removed
39108      */
39109     remove : function(panel, preservePanel){
39110         panel = this.getPanel(panel);
39111         if(!panel){
39112             return null;
39113         }
39114         var e = {};
39115         this.fireEvent("beforeremove", this, panel, e);
39116         if(e.cancel === true){
39117             return null;
39118         }
39119         var panelId = panel.getId();
39120         this.panels.removeKey(panelId);
39121         return panel;
39122     },
39123     
39124     /**
39125      * Returns the panel specified or null if it's not in this region.
39126      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39127      * @return {Roo.ContentPanel}
39128      */
39129     getPanel : function(id){
39130         if(typeof id == "object"){ // must be panel obj
39131             return id;
39132         }
39133         return this.panels.get(id);
39134     },
39135     
39136     /**
39137      * Returns this regions position (north/south/east/west/center).
39138      * @return {String} 
39139      */
39140     getPosition: function(){
39141         return this.position;    
39142     }
39143 });/*
39144  * Based on:
39145  * Ext JS Library 1.1.1
39146  * Copyright(c) 2006-2007, Ext JS, LLC.
39147  *
39148  * Originally Released Under LGPL - original licence link has changed is not relivant.
39149  *
39150  * Fork - LGPL
39151  * <script type="text/javascript">
39152  */
39153  
39154 /**
39155  * @class Roo.bootstrap.layout.Region
39156  * @extends Roo.bootstrap.layout.Basic
39157  * This class represents a region in a layout manager.
39158  
39159  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39160  * @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})
39161  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39162  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39163  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39164  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39165  * @cfg {String}    title           The title for the region (overrides panel titles)
39166  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39167  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39168  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39169  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39170  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39171  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39172  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39173  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39174  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39175  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39176
39177  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39178  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39179  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39180  * @cfg {Number}    width           For East/West panels
39181  * @cfg {Number}    height          For North/South panels
39182  * @cfg {Boolean}   split           To show the splitter
39183  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39184  * 
39185  * @cfg {string}   cls             Extra CSS classes to add to region
39186  * 
39187  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39188  * @cfg {string}   region  the region that it inhabits..
39189  *
39190
39191  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39192  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39193
39194  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39195  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39196  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39197  */
39198 Roo.bootstrap.layout.Region = function(config)
39199 {
39200     this.applyConfig(config);
39201
39202     var mgr = config.mgr;
39203     var pos = config.region;
39204     config.skipConfig = true;
39205     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39206     
39207     if (mgr.el) {
39208         this.onRender(mgr.el);   
39209     }
39210      
39211     this.visible = true;
39212     this.collapsed = false;
39213     this.unrendered_panels = [];
39214 };
39215
39216 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39217
39218     position: '', // set by wrapper (eg. north/south etc..)
39219     unrendered_panels : null,  // unrendered panels.
39220     
39221     tabPosition : false,
39222     
39223     mgr: false, // points to 'Border'
39224     
39225     
39226     createBody : function(){
39227         /** This region's body element 
39228         * @type Roo.Element */
39229         this.bodyEl = this.el.createChild({
39230                 tag: "div",
39231                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39232         });
39233     },
39234
39235     onRender: function(ctr, pos)
39236     {
39237         var dh = Roo.DomHelper;
39238         /** This region's container element 
39239         * @type Roo.Element */
39240         this.el = dh.append(ctr.dom, {
39241                 tag: "div",
39242                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39243             }, true);
39244         /** This region's title element 
39245         * @type Roo.Element */
39246     
39247         this.titleEl = dh.append(this.el.dom,  {
39248                 tag: "div",
39249                 unselectable: "on",
39250                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39251                 children:[
39252                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39253                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39254                 ]
39255             }, true);
39256         
39257         this.titleEl.enableDisplayMode();
39258         /** This region's title text element 
39259         * @type HTMLElement */
39260         this.titleTextEl = this.titleEl.dom.firstChild;
39261         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39262         /*
39263         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39264         this.closeBtn.enableDisplayMode();
39265         this.closeBtn.on("click", this.closeClicked, this);
39266         this.closeBtn.hide();
39267     */
39268         this.createBody(this.config);
39269         if(this.config.hideWhenEmpty){
39270             this.hide();
39271             this.on("paneladded", this.validateVisibility, this);
39272             this.on("panelremoved", this.validateVisibility, this);
39273         }
39274         if(this.autoScroll){
39275             this.bodyEl.setStyle("overflow", "auto");
39276         }else{
39277             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39278         }
39279         //if(c.titlebar !== false){
39280             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39281                 this.titleEl.hide();
39282             }else{
39283                 this.titleEl.show();
39284                 if(this.config.title){
39285                     this.titleTextEl.innerHTML = this.config.title;
39286                 }
39287             }
39288         //}
39289         if(this.config.collapsed){
39290             this.collapse(true);
39291         }
39292         if(this.config.hidden){
39293             this.hide();
39294         }
39295         
39296         if (this.unrendered_panels && this.unrendered_panels.length) {
39297             for (var i =0;i< this.unrendered_panels.length; i++) {
39298                 this.add(this.unrendered_panels[i]);
39299             }
39300             this.unrendered_panels = null;
39301             
39302         }
39303         
39304     },
39305     
39306     applyConfig : function(c)
39307     {
39308         /*
39309          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39310             var dh = Roo.DomHelper;
39311             if(c.titlebar !== false){
39312                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39313                 this.collapseBtn.on("click", this.collapse, this);
39314                 this.collapseBtn.enableDisplayMode();
39315                 /*
39316                 if(c.showPin === true || this.showPin){
39317                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39318                     this.stickBtn.enableDisplayMode();
39319                     this.stickBtn.on("click", this.expand, this);
39320                     this.stickBtn.hide();
39321                 }
39322                 
39323             }
39324             */
39325             /** This region's collapsed element
39326             * @type Roo.Element */
39327             /*
39328              *
39329             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39330                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39331             ]}, true);
39332             
39333             if(c.floatable !== false){
39334                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39335                this.collapsedEl.on("click", this.collapseClick, this);
39336             }
39337
39338             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39339                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39340                    id: "message", unselectable: "on", style:{"float":"left"}});
39341                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39342              }
39343             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39344             this.expandBtn.on("click", this.expand, this);
39345             
39346         }
39347         
39348         if(this.collapseBtn){
39349             this.collapseBtn.setVisible(c.collapsible == true);
39350         }
39351         
39352         this.cmargins = c.cmargins || this.cmargins ||
39353                          (this.position == "west" || this.position == "east" ?
39354                              {top: 0, left: 2, right:2, bottom: 0} :
39355                              {top: 2, left: 0, right:0, bottom: 2});
39356         */
39357         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39358         
39359         
39360         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39361         
39362         this.autoScroll = c.autoScroll || false;
39363         
39364         
39365        
39366         
39367         this.duration = c.duration || .30;
39368         this.slideDuration = c.slideDuration || .45;
39369         this.config = c;
39370        
39371     },
39372     /**
39373      * Returns true if this region is currently visible.
39374      * @return {Boolean}
39375      */
39376     isVisible : function(){
39377         return this.visible;
39378     },
39379
39380     /**
39381      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39382      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39383      */
39384     //setCollapsedTitle : function(title){
39385     //    title = title || "&#160;";
39386      //   if(this.collapsedTitleTextEl){
39387       //      this.collapsedTitleTextEl.innerHTML = title;
39388        // }
39389     //},
39390
39391     getBox : function(){
39392         var b;
39393       //  if(!this.collapsed){
39394             b = this.el.getBox(false, true);
39395        // }else{
39396           //  b = this.collapsedEl.getBox(false, true);
39397         //}
39398         return b;
39399     },
39400
39401     getMargins : function(){
39402         return this.margins;
39403         //return this.collapsed ? this.cmargins : this.margins;
39404     },
39405 /*
39406     highlight : function(){
39407         this.el.addClass("x-layout-panel-dragover");
39408     },
39409
39410     unhighlight : function(){
39411         this.el.removeClass("x-layout-panel-dragover");
39412     },
39413 */
39414     updateBox : function(box)
39415     {
39416         if (!this.bodyEl) {
39417             return; // not rendered yet..
39418         }
39419         
39420         this.box = box;
39421         if(!this.collapsed){
39422             this.el.dom.style.left = box.x + "px";
39423             this.el.dom.style.top = box.y + "px";
39424             this.updateBody(box.width, box.height);
39425         }else{
39426             this.collapsedEl.dom.style.left = box.x + "px";
39427             this.collapsedEl.dom.style.top = box.y + "px";
39428             this.collapsedEl.setSize(box.width, box.height);
39429         }
39430         if(this.tabs){
39431             this.tabs.autoSizeTabs();
39432         }
39433     },
39434
39435     updateBody : function(w, h)
39436     {
39437         if(w !== null){
39438             this.el.setWidth(w);
39439             w -= this.el.getBorderWidth("rl");
39440             if(this.config.adjustments){
39441                 w += this.config.adjustments[0];
39442             }
39443         }
39444         if(h !== null && h > 0){
39445             this.el.setHeight(h);
39446             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39447             h -= this.el.getBorderWidth("tb");
39448             if(this.config.adjustments){
39449                 h += this.config.adjustments[1];
39450             }
39451             this.bodyEl.setHeight(h);
39452             if(this.tabs){
39453                 h = this.tabs.syncHeight(h);
39454             }
39455         }
39456         if(this.panelSize){
39457             w = w !== null ? w : this.panelSize.width;
39458             h = h !== null ? h : this.panelSize.height;
39459         }
39460         if(this.activePanel){
39461             var el = this.activePanel.getEl();
39462             w = w !== null ? w : el.getWidth();
39463             h = h !== null ? h : el.getHeight();
39464             this.panelSize = {width: w, height: h};
39465             this.activePanel.setSize(w, h);
39466         }
39467         if(Roo.isIE && this.tabs){
39468             this.tabs.el.repaint();
39469         }
39470     },
39471
39472     /**
39473      * Returns the container element for this region.
39474      * @return {Roo.Element}
39475      */
39476     getEl : function(){
39477         return this.el;
39478     },
39479
39480     /**
39481      * Hides this region.
39482      */
39483     hide : function(){
39484         //if(!this.collapsed){
39485             this.el.dom.style.left = "-2000px";
39486             this.el.hide();
39487         //}else{
39488          //   this.collapsedEl.dom.style.left = "-2000px";
39489          //   this.collapsedEl.hide();
39490        // }
39491         this.visible = false;
39492         this.fireEvent("visibilitychange", this, false);
39493     },
39494
39495     /**
39496      * Shows this region if it was previously hidden.
39497      */
39498     show : function(){
39499         //if(!this.collapsed){
39500             this.el.show();
39501         //}else{
39502         //    this.collapsedEl.show();
39503        // }
39504         this.visible = true;
39505         this.fireEvent("visibilitychange", this, true);
39506     },
39507 /*
39508     closeClicked : function(){
39509         if(this.activePanel){
39510             this.remove(this.activePanel);
39511         }
39512     },
39513
39514     collapseClick : function(e){
39515         if(this.isSlid){
39516            e.stopPropagation();
39517            this.slideIn();
39518         }else{
39519            e.stopPropagation();
39520            this.slideOut();
39521         }
39522     },
39523 */
39524     /**
39525      * Collapses this region.
39526      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39527      */
39528     /*
39529     collapse : function(skipAnim, skipCheck = false){
39530         if(this.collapsed) {
39531             return;
39532         }
39533         
39534         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39535             
39536             this.collapsed = true;
39537             if(this.split){
39538                 this.split.el.hide();
39539             }
39540             if(this.config.animate && skipAnim !== true){
39541                 this.fireEvent("invalidated", this);
39542                 this.animateCollapse();
39543             }else{
39544                 this.el.setLocation(-20000,-20000);
39545                 this.el.hide();
39546                 this.collapsedEl.show();
39547                 this.fireEvent("collapsed", this);
39548                 this.fireEvent("invalidated", this);
39549             }
39550         }
39551         
39552     },
39553 */
39554     animateCollapse : function(){
39555         // overridden
39556     },
39557
39558     /**
39559      * Expands this region if it was previously collapsed.
39560      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39561      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39562      */
39563     /*
39564     expand : function(e, skipAnim){
39565         if(e) {
39566             e.stopPropagation();
39567         }
39568         if(!this.collapsed || this.el.hasActiveFx()) {
39569             return;
39570         }
39571         if(this.isSlid){
39572             this.afterSlideIn();
39573             skipAnim = true;
39574         }
39575         this.collapsed = false;
39576         if(this.config.animate && skipAnim !== true){
39577             this.animateExpand();
39578         }else{
39579             this.el.show();
39580             if(this.split){
39581                 this.split.el.show();
39582             }
39583             this.collapsedEl.setLocation(-2000,-2000);
39584             this.collapsedEl.hide();
39585             this.fireEvent("invalidated", this);
39586             this.fireEvent("expanded", this);
39587         }
39588     },
39589 */
39590     animateExpand : function(){
39591         // overridden
39592     },
39593
39594     initTabs : function()
39595     {
39596         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39597         
39598         var ts = new Roo.bootstrap.panel.Tabs({
39599             el: this.bodyEl.dom,
39600             region : this,
39601             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39602             disableTooltips: this.config.disableTabTips,
39603             toolbar : this.config.toolbar
39604         });
39605         
39606         if(this.config.hideTabs){
39607             ts.stripWrap.setDisplayed(false);
39608         }
39609         this.tabs = ts;
39610         ts.resizeTabs = this.config.resizeTabs === true;
39611         ts.minTabWidth = this.config.minTabWidth || 40;
39612         ts.maxTabWidth = this.config.maxTabWidth || 250;
39613         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39614         ts.monitorResize = false;
39615         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39616         ts.bodyEl.addClass('roo-layout-tabs-body');
39617         this.panels.each(this.initPanelAsTab, this);
39618     },
39619
39620     initPanelAsTab : function(panel){
39621         var ti = this.tabs.addTab(
39622             panel.getEl().id,
39623             panel.getTitle(),
39624             null,
39625             this.config.closeOnTab && panel.isClosable(),
39626             panel.tpl
39627         );
39628         if(panel.tabTip !== undefined){
39629             ti.setTooltip(panel.tabTip);
39630         }
39631         ti.on("activate", function(){
39632               this.setActivePanel(panel);
39633         }, this);
39634         
39635         if(this.config.closeOnTab){
39636             ti.on("beforeclose", function(t, e){
39637                 e.cancel = true;
39638                 this.remove(panel);
39639             }, this);
39640         }
39641         
39642         panel.tabItem = ti;
39643         
39644         return ti;
39645     },
39646
39647     updatePanelTitle : function(panel, title)
39648     {
39649         if(this.activePanel == panel){
39650             this.updateTitle(title);
39651         }
39652         if(this.tabs){
39653             var ti = this.tabs.getTab(panel.getEl().id);
39654             ti.setText(title);
39655             if(panel.tabTip !== undefined){
39656                 ti.setTooltip(panel.tabTip);
39657             }
39658         }
39659     },
39660
39661     updateTitle : function(title){
39662         if(this.titleTextEl && !this.config.title){
39663             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39664         }
39665     },
39666
39667     setActivePanel : function(panel)
39668     {
39669         panel = this.getPanel(panel);
39670         if(this.activePanel && this.activePanel != panel){
39671             if(this.activePanel.setActiveState(false) === false){
39672                 return;
39673             }
39674         }
39675         this.activePanel = panel;
39676         panel.setActiveState(true);
39677         if(this.panelSize){
39678             panel.setSize(this.panelSize.width, this.panelSize.height);
39679         }
39680         if(this.closeBtn){
39681             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39682         }
39683         this.updateTitle(panel.getTitle());
39684         if(this.tabs){
39685             this.fireEvent("invalidated", this);
39686         }
39687         this.fireEvent("panelactivated", this, panel);
39688     },
39689
39690     /**
39691      * Shows the specified panel.
39692      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39693      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39694      */
39695     showPanel : function(panel)
39696     {
39697         panel = this.getPanel(panel);
39698         if(panel){
39699             if(this.tabs){
39700                 var tab = this.tabs.getTab(panel.getEl().id);
39701                 if(tab.isHidden()){
39702                     this.tabs.unhideTab(tab.id);
39703                 }
39704                 tab.activate();
39705             }else{
39706                 this.setActivePanel(panel);
39707             }
39708         }
39709         return panel;
39710     },
39711
39712     /**
39713      * Get the active panel for this region.
39714      * @return {Roo.ContentPanel} The active panel or null
39715      */
39716     getActivePanel : function(){
39717         return this.activePanel;
39718     },
39719
39720     validateVisibility : function(){
39721         if(this.panels.getCount() < 1){
39722             this.updateTitle("&#160;");
39723             this.closeBtn.hide();
39724             this.hide();
39725         }else{
39726             if(!this.isVisible()){
39727                 this.show();
39728             }
39729         }
39730     },
39731
39732     /**
39733      * Adds the passed ContentPanel(s) to this region.
39734      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39735      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39736      */
39737     add : function(panel)
39738     {
39739         if(arguments.length > 1){
39740             for(var i = 0, len = arguments.length; i < len; i++) {
39741                 this.add(arguments[i]);
39742             }
39743             return null;
39744         }
39745         
39746         // if we have not been rendered yet, then we can not really do much of this..
39747         if (!this.bodyEl) {
39748             this.unrendered_panels.push(panel);
39749             return panel;
39750         }
39751         
39752         
39753         
39754         
39755         if(this.hasPanel(panel)){
39756             this.showPanel(panel);
39757             return panel;
39758         }
39759         panel.setRegion(this);
39760         this.panels.add(panel);
39761        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39762             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39763             // and hide them... ???
39764             this.bodyEl.dom.appendChild(panel.getEl().dom);
39765             if(panel.background !== true){
39766                 this.setActivePanel(panel);
39767             }
39768             this.fireEvent("paneladded", this, panel);
39769             return panel;
39770         }
39771         */
39772         if(!this.tabs){
39773             this.initTabs();
39774         }else{
39775             this.initPanelAsTab(panel);
39776         }
39777         
39778         
39779         if(panel.background !== true){
39780             this.tabs.activate(panel.getEl().id);
39781         }
39782         this.fireEvent("paneladded", this, panel);
39783         return panel;
39784     },
39785
39786     /**
39787      * Hides the tab for the specified panel.
39788      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39789      */
39790     hidePanel : function(panel){
39791         if(this.tabs && (panel = this.getPanel(panel))){
39792             this.tabs.hideTab(panel.getEl().id);
39793         }
39794     },
39795
39796     /**
39797      * Unhides the tab for a previously hidden panel.
39798      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39799      */
39800     unhidePanel : function(panel){
39801         if(this.tabs && (panel = this.getPanel(panel))){
39802             this.tabs.unhideTab(panel.getEl().id);
39803         }
39804     },
39805
39806     clearPanels : function(){
39807         while(this.panels.getCount() > 0){
39808              this.remove(this.panels.first());
39809         }
39810     },
39811
39812     /**
39813      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39814      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39815      * @param {Boolean} preservePanel Overrides the config preservePanel option
39816      * @return {Roo.ContentPanel} The panel that was removed
39817      */
39818     remove : function(panel, preservePanel)
39819     {
39820         panel = this.getPanel(panel);
39821         if(!panel){
39822             return null;
39823         }
39824         var e = {};
39825         this.fireEvent("beforeremove", this, panel, e);
39826         if(e.cancel === true){
39827             return null;
39828         }
39829         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39830         var panelId = panel.getId();
39831         this.panels.removeKey(panelId);
39832         if(preservePanel){
39833             document.body.appendChild(panel.getEl().dom);
39834         }
39835         if(this.tabs){
39836             this.tabs.removeTab(panel.getEl().id);
39837         }else if (!preservePanel){
39838             this.bodyEl.dom.removeChild(panel.getEl().dom);
39839         }
39840         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39841             var p = this.panels.first();
39842             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39843             tempEl.appendChild(p.getEl().dom);
39844             this.bodyEl.update("");
39845             this.bodyEl.dom.appendChild(p.getEl().dom);
39846             tempEl = null;
39847             this.updateTitle(p.getTitle());
39848             this.tabs = null;
39849             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39850             this.setActivePanel(p);
39851         }
39852         panel.setRegion(null);
39853         if(this.activePanel == panel){
39854             this.activePanel = null;
39855         }
39856         if(this.config.autoDestroy !== false && preservePanel !== true){
39857             try{panel.destroy();}catch(e){}
39858         }
39859         this.fireEvent("panelremoved", this, panel);
39860         return panel;
39861     },
39862
39863     /**
39864      * Returns the TabPanel component used by this region
39865      * @return {Roo.TabPanel}
39866      */
39867     getTabs : function(){
39868         return this.tabs;
39869     },
39870
39871     createTool : function(parentEl, className){
39872         var btn = Roo.DomHelper.append(parentEl, {
39873             tag: "div",
39874             cls: "x-layout-tools-button",
39875             children: [ {
39876                 tag: "div",
39877                 cls: "roo-layout-tools-button-inner " + className,
39878                 html: "&#160;"
39879             }]
39880         }, true);
39881         btn.addClassOnOver("roo-layout-tools-button-over");
39882         return btn;
39883     }
39884 });/*
39885  * Based on:
39886  * Ext JS Library 1.1.1
39887  * Copyright(c) 2006-2007, Ext JS, LLC.
39888  *
39889  * Originally Released Under LGPL - original licence link has changed is not relivant.
39890  *
39891  * Fork - LGPL
39892  * <script type="text/javascript">
39893  */
39894  
39895
39896
39897 /**
39898  * @class Roo.SplitLayoutRegion
39899  * @extends Roo.LayoutRegion
39900  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39901  */
39902 Roo.bootstrap.layout.Split = function(config){
39903     this.cursor = config.cursor;
39904     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39905 };
39906
39907 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39908 {
39909     splitTip : "Drag to resize.",
39910     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39911     useSplitTips : false,
39912
39913     applyConfig : function(config){
39914         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39915     },
39916     
39917     onRender : function(ctr,pos) {
39918         
39919         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39920         if(!this.config.split){
39921             return;
39922         }
39923         if(!this.split){
39924             
39925             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39926                             tag: "div",
39927                             id: this.el.id + "-split",
39928                             cls: "roo-layout-split roo-layout-split-"+this.position,
39929                             html: "&#160;"
39930             });
39931             /** The SplitBar for this region 
39932             * @type Roo.SplitBar */
39933             // does not exist yet...
39934             Roo.log([this.position, this.orientation]);
39935             
39936             this.split = new Roo.bootstrap.SplitBar({
39937                 dragElement : splitEl,
39938                 resizingElement: this.el,
39939                 orientation : this.orientation
39940             });
39941             
39942             this.split.on("moved", this.onSplitMove, this);
39943             this.split.useShim = this.config.useShim === true;
39944             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39945             if(this.useSplitTips){
39946                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39947             }
39948             //if(config.collapsible){
39949             //    this.split.el.on("dblclick", this.collapse,  this);
39950             //}
39951         }
39952         if(typeof this.config.minSize != "undefined"){
39953             this.split.minSize = this.config.minSize;
39954         }
39955         if(typeof this.config.maxSize != "undefined"){
39956             this.split.maxSize = this.config.maxSize;
39957         }
39958         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39959             this.hideSplitter();
39960         }
39961         
39962     },
39963
39964     getHMaxSize : function(){
39965          var cmax = this.config.maxSize || 10000;
39966          var center = this.mgr.getRegion("center");
39967          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39968     },
39969
39970     getVMaxSize : function(){
39971          var cmax = this.config.maxSize || 10000;
39972          var center = this.mgr.getRegion("center");
39973          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39974     },
39975
39976     onSplitMove : function(split, newSize){
39977         this.fireEvent("resized", this, newSize);
39978     },
39979     
39980     /** 
39981      * Returns the {@link Roo.SplitBar} for this region.
39982      * @return {Roo.SplitBar}
39983      */
39984     getSplitBar : function(){
39985         return this.split;
39986     },
39987     
39988     hide : function(){
39989         this.hideSplitter();
39990         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39991     },
39992
39993     hideSplitter : function(){
39994         if(this.split){
39995             this.split.el.setLocation(-2000,-2000);
39996             this.split.el.hide();
39997         }
39998     },
39999
40000     show : function(){
40001         if(this.split){
40002             this.split.el.show();
40003         }
40004         Roo.bootstrap.layout.Split.superclass.show.call(this);
40005     },
40006     
40007     beforeSlide: function(){
40008         if(Roo.isGecko){// firefox overflow auto bug workaround
40009             this.bodyEl.clip();
40010             if(this.tabs) {
40011                 this.tabs.bodyEl.clip();
40012             }
40013             if(this.activePanel){
40014                 this.activePanel.getEl().clip();
40015                 
40016                 if(this.activePanel.beforeSlide){
40017                     this.activePanel.beforeSlide();
40018                 }
40019             }
40020         }
40021     },
40022     
40023     afterSlide : function(){
40024         if(Roo.isGecko){// firefox overflow auto bug workaround
40025             this.bodyEl.unclip();
40026             if(this.tabs) {
40027                 this.tabs.bodyEl.unclip();
40028             }
40029             if(this.activePanel){
40030                 this.activePanel.getEl().unclip();
40031                 if(this.activePanel.afterSlide){
40032                     this.activePanel.afterSlide();
40033                 }
40034             }
40035         }
40036     },
40037
40038     initAutoHide : function(){
40039         if(this.autoHide !== false){
40040             if(!this.autoHideHd){
40041                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40042                 this.autoHideHd = {
40043                     "mouseout": function(e){
40044                         if(!e.within(this.el, true)){
40045                             st.delay(500);
40046                         }
40047                     },
40048                     "mouseover" : function(e){
40049                         st.cancel();
40050                     },
40051                     scope : this
40052                 };
40053             }
40054             this.el.on(this.autoHideHd);
40055         }
40056     },
40057
40058     clearAutoHide : function(){
40059         if(this.autoHide !== false){
40060             this.el.un("mouseout", this.autoHideHd.mouseout);
40061             this.el.un("mouseover", this.autoHideHd.mouseover);
40062         }
40063     },
40064
40065     clearMonitor : function(){
40066         Roo.get(document).un("click", this.slideInIf, this);
40067     },
40068
40069     // these names are backwards but not changed for compat
40070     slideOut : function(){
40071         if(this.isSlid || this.el.hasActiveFx()){
40072             return;
40073         }
40074         this.isSlid = true;
40075         if(this.collapseBtn){
40076             this.collapseBtn.hide();
40077         }
40078         this.closeBtnState = this.closeBtn.getStyle('display');
40079         this.closeBtn.hide();
40080         if(this.stickBtn){
40081             this.stickBtn.show();
40082         }
40083         this.el.show();
40084         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40085         this.beforeSlide();
40086         this.el.setStyle("z-index", 10001);
40087         this.el.slideIn(this.getSlideAnchor(), {
40088             callback: function(){
40089                 this.afterSlide();
40090                 this.initAutoHide();
40091                 Roo.get(document).on("click", this.slideInIf, this);
40092                 this.fireEvent("slideshow", this);
40093             },
40094             scope: this,
40095             block: true
40096         });
40097     },
40098
40099     afterSlideIn : function(){
40100         this.clearAutoHide();
40101         this.isSlid = false;
40102         this.clearMonitor();
40103         this.el.setStyle("z-index", "");
40104         if(this.collapseBtn){
40105             this.collapseBtn.show();
40106         }
40107         this.closeBtn.setStyle('display', this.closeBtnState);
40108         if(this.stickBtn){
40109             this.stickBtn.hide();
40110         }
40111         this.fireEvent("slidehide", this);
40112     },
40113
40114     slideIn : function(cb){
40115         if(!this.isSlid || this.el.hasActiveFx()){
40116             Roo.callback(cb);
40117             return;
40118         }
40119         this.isSlid = false;
40120         this.beforeSlide();
40121         this.el.slideOut(this.getSlideAnchor(), {
40122             callback: function(){
40123                 this.el.setLeftTop(-10000, -10000);
40124                 this.afterSlide();
40125                 this.afterSlideIn();
40126                 Roo.callback(cb);
40127             },
40128             scope: this,
40129             block: true
40130         });
40131     },
40132     
40133     slideInIf : function(e){
40134         if(!e.within(this.el)){
40135             this.slideIn();
40136         }
40137     },
40138
40139     animateCollapse : function(){
40140         this.beforeSlide();
40141         this.el.setStyle("z-index", 20000);
40142         var anchor = this.getSlideAnchor();
40143         this.el.slideOut(anchor, {
40144             callback : function(){
40145                 this.el.setStyle("z-index", "");
40146                 this.collapsedEl.slideIn(anchor, {duration:.3});
40147                 this.afterSlide();
40148                 this.el.setLocation(-10000,-10000);
40149                 this.el.hide();
40150                 this.fireEvent("collapsed", this);
40151             },
40152             scope: this,
40153             block: true
40154         });
40155     },
40156
40157     animateExpand : function(){
40158         this.beforeSlide();
40159         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40160         this.el.setStyle("z-index", 20000);
40161         this.collapsedEl.hide({
40162             duration:.1
40163         });
40164         this.el.slideIn(this.getSlideAnchor(), {
40165             callback : function(){
40166                 this.el.setStyle("z-index", "");
40167                 this.afterSlide();
40168                 if(this.split){
40169                     this.split.el.show();
40170                 }
40171                 this.fireEvent("invalidated", this);
40172                 this.fireEvent("expanded", this);
40173             },
40174             scope: this,
40175             block: true
40176         });
40177     },
40178
40179     anchors : {
40180         "west" : "left",
40181         "east" : "right",
40182         "north" : "top",
40183         "south" : "bottom"
40184     },
40185
40186     sanchors : {
40187         "west" : "l",
40188         "east" : "r",
40189         "north" : "t",
40190         "south" : "b"
40191     },
40192
40193     canchors : {
40194         "west" : "tl-tr",
40195         "east" : "tr-tl",
40196         "north" : "tl-bl",
40197         "south" : "bl-tl"
40198     },
40199
40200     getAnchor : function(){
40201         return this.anchors[this.position];
40202     },
40203
40204     getCollapseAnchor : function(){
40205         return this.canchors[this.position];
40206     },
40207
40208     getSlideAnchor : function(){
40209         return this.sanchors[this.position];
40210     },
40211
40212     getAlignAdj : function(){
40213         var cm = this.cmargins;
40214         switch(this.position){
40215             case "west":
40216                 return [0, 0];
40217             break;
40218             case "east":
40219                 return [0, 0];
40220             break;
40221             case "north":
40222                 return [0, 0];
40223             break;
40224             case "south":
40225                 return [0, 0];
40226             break;
40227         }
40228     },
40229
40230     getExpandAdj : function(){
40231         var c = this.collapsedEl, cm = this.cmargins;
40232         switch(this.position){
40233             case "west":
40234                 return [-(cm.right+c.getWidth()+cm.left), 0];
40235             break;
40236             case "east":
40237                 return [cm.right+c.getWidth()+cm.left, 0];
40238             break;
40239             case "north":
40240                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40241             break;
40242             case "south":
40243                 return [0, cm.top+cm.bottom+c.getHeight()];
40244             break;
40245         }
40246     }
40247 });/*
40248  * Based on:
40249  * Ext JS Library 1.1.1
40250  * Copyright(c) 2006-2007, Ext JS, LLC.
40251  *
40252  * Originally Released Under LGPL - original licence link has changed is not relivant.
40253  *
40254  * Fork - LGPL
40255  * <script type="text/javascript">
40256  */
40257 /*
40258  * These classes are private internal classes
40259  */
40260 Roo.bootstrap.layout.Center = function(config){
40261     config.region = "center";
40262     Roo.bootstrap.layout.Region.call(this, config);
40263     this.visible = true;
40264     this.minWidth = config.minWidth || 20;
40265     this.minHeight = config.minHeight || 20;
40266 };
40267
40268 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40269     hide : function(){
40270         // center panel can't be hidden
40271     },
40272     
40273     show : function(){
40274         // center panel can't be hidden
40275     },
40276     
40277     getMinWidth: function(){
40278         return this.minWidth;
40279     },
40280     
40281     getMinHeight: function(){
40282         return this.minHeight;
40283     }
40284 });
40285
40286
40287
40288
40289  
40290
40291
40292
40293
40294
40295
40296 Roo.bootstrap.layout.North = function(config)
40297 {
40298     config.region = 'north';
40299     config.cursor = 'n-resize';
40300     
40301     Roo.bootstrap.layout.Split.call(this, config);
40302     
40303     
40304     if(this.split){
40305         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40306         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40307         this.split.el.addClass("roo-layout-split-v");
40308     }
40309     //var size = config.initialSize || config.height;
40310     //if(this.el && typeof size != "undefined"){
40311     //    this.el.setHeight(size);
40312     //}
40313 };
40314 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40315 {
40316     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40317      
40318      
40319     onRender : function(ctr, pos)
40320     {
40321         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40322         var size = this.config.initialSize || this.config.height;
40323         if(this.el && typeof size != "undefined"){
40324             this.el.setHeight(size);
40325         }
40326     
40327     },
40328     
40329     getBox : function(){
40330         if(this.collapsed){
40331             return this.collapsedEl.getBox();
40332         }
40333         var box = this.el.getBox();
40334         if(this.split){
40335             box.height += this.split.el.getHeight();
40336         }
40337         return box;
40338     },
40339     
40340     updateBox : function(box){
40341         if(this.split && !this.collapsed){
40342             box.height -= this.split.el.getHeight();
40343             this.split.el.setLeft(box.x);
40344             this.split.el.setTop(box.y+box.height);
40345             this.split.el.setWidth(box.width);
40346         }
40347         if(this.collapsed){
40348             this.updateBody(box.width, null);
40349         }
40350         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40351     }
40352 });
40353
40354
40355
40356
40357
40358 Roo.bootstrap.layout.South = function(config){
40359     config.region = 'south';
40360     config.cursor = 's-resize';
40361     Roo.bootstrap.layout.Split.call(this, config);
40362     if(this.split){
40363         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40364         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40365         this.split.el.addClass("roo-layout-split-v");
40366     }
40367     
40368 };
40369
40370 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40371     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40372     
40373     onRender : function(ctr, pos)
40374     {
40375         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40376         var size = this.config.initialSize || this.config.height;
40377         if(this.el && typeof size != "undefined"){
40378             this.el.setHeight(size);
40379         }
40380     
40381     },
40382     
40383     getBox : function(){
40384         if(this.collapsed){
40385             return this.collapsedEl.getBox();
40386         }
40387         var box = this.el.getBox();
40388         if(this.split){
40389             var sh = this.split.el.getHeight();
40390             box.height += sh;
40391             box.y -= sh;
40392         }
40393         return box;
40394     },
40395     
40396     updateBox : function(box){
40397         if(this.split && !this.collapsed){
40398             var sh = this.split.el.getHeight();
40399             box.height -= sh;
40400             box.y += sh;
40401             this.split.el.setLeft(box.x);
40402             this.split.el.setTop(box.y-sh);
40403             this.split.el.setWidth(box.width);
40404         }
40405         if(this.collapsed){
40406             this.updateBody(box.width, null);
40407         }
40408         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40409     }
40410 });
40411
40412 Roo.bootstrap.layout.East = function(config){
40413     config.region = "east";
40414     config.cursor = "e-resize";
40415     Roo.bootstrap.layout.Split.call(this, config);
40416     if(this.split){
40417         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40418         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40419         this.split.el.addClass("roo-layout-split-h");
40420     }
40421     
40422 };
40423 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40424     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40425     
40426     onRender : function(ctr, pos)
40427     {
40428         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40429         var size = this.config.initialSize || this.config.width;
40430         if(this.el && typeof size != "undefined"){
40431             this.el.setWidth(size);
40432         }
40433     
40434     },
40435     
40436     getBox : function(){
40437         if(this.collapsed){
40438             return this.collapsedEl.getBox();
40439         }
40440         var box = this.el.getBox();
40441         if(this.split){
40442             var sw = this.split.el.getWidth();
40443             box.width += sw;
40444             box.x -= sw;
40445         }
40446         return box;
40447     },
40448
40449     updateBox : function(box){
40450         if(this.split && !this.collapsed){
40451             var sw = this.split.el.getWidth();
40452             box.width -= sw;
40453             this.split.el.setLeft(box.x);
40454             this.split.el.setTop(box.y);
40455             this.split.el.setHeight(box.height);
40456             box.x += sw;
40457         }
40458         if(this.collapsed){
40459             this.updateBody(null, box.height);
40460         }
40461         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40462     }
40463 });
40464
40465 Roo.bootstrap.layout.West = function(config){
40466     config.region = "west";
40467     config.cursor = "w-resize";
40468     
40469     Roo.bootstrap.layout.Split.call(this, config);
40470     if(this.split){
40471         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40472         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40473         this.split.el.addClass("roo-layout-split-h");
40474     }
40475     
40476 };
40477 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40478     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40479     
40480     onRender: function(ctr, pos)
40481     {
40482         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40483         var size = this.config.initialSize || this.config.width;
40484         if(typeof size != "undefined"){
40485             this.el.setWidth(size);
40486         }
40487     },
40488     
40489     getBox : function(){
40490         if(this.collapsed){
40491             return this.collapsedEl.getBox();
40492         }
40493         var box = this.el.getBox();
40494         if (box.width == 0) {
40495             box.width = this.config.width; // kludge?
40496         }
40497         if(this.split){
40498             box.width += this.split.el.getWidth();
40499         }
40500         return box;
40501     },
40502     
40503     updateBox : function(box){
40504         if(this.split && !this.collapsed){
40505             var sw = this.split.el.getWidth();
40506             box.width -= sw;
40507             this.split.el.setLeft(box.x+box.width);
40508             this.split.el.setTop(box.y);
40509             this.split.el.setHeight(box.height);
40510         }
40511         if(this.collapsed){
40512             this.updateBody(null, box.height);
40513         }
40514         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40515     }
40516 });Roo.namespace("Roo.bootstrap.panel");/*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526 /**
40527  * @class Roo.ContentPanel
40528  * @extends Roo.util.Observable
40529  * A basic ContentPanel element.
40530  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40531  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40532  * @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
40533  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40534  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40535  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40536  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40537  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40538  * @cfg {String} title          The title for this panel
40539  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40540  * @cfg {String} url            Calls {@link #setUrl} with this value
40541  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40542  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40543  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40544  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40545  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40546  * @cfg {Boolean} badges render the badges
40547  * @cfg {String} cls  extra classes to use  
40548  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40549
40550  * @constructor
40551  * Create a new ContentPanel.
40552  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40553  * @param {String/Object} config A string to set only the title or a config object
40554  * @param {String} content (optional) Set the HTML content for this panel
40555  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40556  */
40557 Roo.bootstrap.panel.Content = function( config){
40558     
40559     this.tpl = config.tpl || false;
40560     
40561     var el = config.el;
40562     var content = config.content;
40563
40564     if(config.autoCreate){ // xtype is available if this is called from factory
40565         el = Roo.id();
40566     }
40567     this.el = Roo.get(el);
40568     if(!this.el && config && config.autoCreate){
40569         if(typeof config.autoCreate == "object"){
40570             if(!config.autoCreate.id){
40571                 config.autoCreate.id = config.id||el;
40572             }
40573             this.el = Roo.DomHelper.append(document.body,
40574                         config.autoCreate, true);
40575         }else{
40576             var elcfg =  {
40577                 tag: "div",
40578                 cls: (config.cls || '') +
40579                     (config.background ? ' bg-' + config.background : '') +
40580                     " roo-layout-inactive-content",
40581                 id: config.id||el
40582             };
40583             if (config.iframe) {
40584                 elcfg.cn = [
40585                     {
40586                         tag : 'iframe',
40587                         style : 'border: 0px',
40588                         src : 'about:blank'
40589                     }
40590                 ];
40591             }
40592               
40593             if (config.html) {
40594                 elcfg.html = config.html;
40595                 
40596             }
40597                         
40598             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40599             if (config.iframe) {
40600                 this.iframeEl = this.el.select('iframe',true).first();
40601             }
40602             
40603         }
40604     } 
40605     this.closable = false;
40606     this.loaded = false;
40607     this.active = false;
40608    
40609       
40610     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40611         
40612         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40613         
40614         this.wrapEl = this.el; //this.el.wrap();
40615         var ti = [];
40616         if (config.toolbar.items) {
40617             ti = config.toolbar.items ;
40618             delete config.toolbar.items ;
40619         }
40620         
40621         var nitems = [];
40622         this.toolbar.render(this.wrapEl, 'before');
40623         for(var i =0;i < ti.length;i++) {
40624           //  Roo.log(['add child', items[i]]);
40625             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40626         }
40627         this.toolbar.items = nitems;
40628         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40629         delete config.toolbar;
40630         
40631     }
40632     /*
40633     // xtype created footer. - not sure if will work as we normally have to render first..
40634     if (this.footer && !this.footer.el && this.footer.xtype) {
40635         if (!this.wrapEl) {
40636             this.wrapEl = this.el.wrap();
40637         }
40638     
40639         this.footer.container = this.wrapEl.createChild();
40640          
40641         this.footer = Roo.factory(this.footer, Roo);
40642         
40643     }
40644     */
40645     
40646      if(typeof config == "string"){
40647         this.title = config;
40648     }else{
40649         Roo.apply(this, config);
40650     }
40651     
40652     if(this.resizeEl){
40653         this.resizeEl = Roo.get(this.resizeEl, true);
40654     }else{
40655         this.resizeEl = this.el;
40656     }
40657     // handle view.xtype
40658     
40659  
40660     
40661     
40662     this.addEvents({
40663         /**
40664          * @event activate
40665          * Fires when this panel is activated. 
40666          * @param {Roo.ContentPanel} this
40667          */
40668         "activate" : true,
40669         /**
40670          * @event deactivate
40671          * Fires when this panel is activated. 
40672          * @param {Roo.ContentPanel} this
40673          */
40674         "deactivate" : true,
40675
40676         /**
40677          * @event resize
40678          * Fires when this panel is resized if fitToFrame is true.
40679          * @param {Roo.ContentPanel} this
40680          * @param {Number} width The width after any component adjustments
40681          * @param {Number} height The height after any component adjustments
40682          */
40683         "resize" : true,
40684         
40685          /**
40686          * @event render
40687          * Fires when this tab is created
40688          * @param {Roo.ContentPanel} this
40689          */
40690         "render" : true,
40691         
40692           /**
40693          * @event scroll
40694          * Fires when this content is scrolled
40695          * @param {Roo.ContentPanel} this
40696          * @param {Event} scrollEvent
40697          */
40698         "scroll" : true
40699         
40700         
40701         
40702     });
40703     
40704
40705     
40706     
40707     if(this.autoScroll && !this.iframe){
40708         this.resizeEl.setStyle("overflow", "auto");
40709         this.resizeEl.on('scroll', this.onScroll, this);
40710     } else {
40711         // fix randome scrolling
40712         //this.el.on('scroll', function() {
40713         //    Roo.log('fix random scolling');
40714         //    this.scrollTo('top',0); 
40715         //});
40716     }
40717     content = content || this.content;
40718     if(content){
40719         this.setContent(content);
40720     }
40721     if(config && config.url){
40722         this.setUrl(this.url, this.params, this.loadOnce);
40723     }
40724     
40725     
40726     
40727     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40728     
40729     if (this.view && typeof(this.view.xtype) != 'undefined') {
40730         this.view.el = this.el.appendChild(document.createElement("div"));
40731         this.view = Roo.factory(this.view); 
40732         this.view.render  &&  this.view.render(false, '');  
40733     }
40734     
40735     
40736     this.fireEvent('render', this);
40737 };
40738
40739 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40740     
40741     cls : '',
40742     background : '',
40743     
40744     tabTip : '',
40745     
40746     iframe : false,
40747     iframeEl : false,
40748     
40749     /* Resize Element - use this to work out scroll etc. */
40750     resizeEl : false,
40751     
40752     setRegion : function(region){
40753         this.region = region;
40754         this.setActiveClass(region && !this.background);
40755     },
40756     
40757     
40758     setActiveClass: function(state)
40759     {
40760         if(state){
40761            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40762            this.el.setStyle('position','relative');
40763         }else{
40764            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40765            this.el.setStyle('position', 'absolute');
40766         } 
40767     },
40768     
40769     /**
40770      * Returns the toolbar for this Panel if one was configured. 
40771      * @return {Roo.Toolbar} 
40772      */
40773     getToolbar : function(){
40774         return this.toolbar;
40775     },
40776     
40777     setActiveState : function(active)
40778     {
40779         this.active = active;
40780         this.setActiveClass(active);
40781         if(!active){
40782             if(this.fireEvent("deactivate", this) === false){
40783                 return false;
40784             }
40785             return true;
40786         }
40787         this.fireEvent("activate", this);
40788         return true;
40789     },
40790     /**
40791      * Updates this panel's element (not for iframe)
40792      * @param {String} content The new content
40793      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40794     */
40795     setContent : function(content, loadScripts){
40796         if (this.iframe) {
40797             return;
40798         }
40799         
40800         this.el.update(content, loadScripts);
40801     },
40802
40803     ignoreResize : function(w, h){
40804         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40805             return true;
40806         }else{
40807             this.lastSize = {width: w, height: h};
40808             return false;
40809         }
40810     },
40811     /**
40812      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40813      * @return {Roo.UpdateManager} The UpdateManager
40814      */
40815     getUpdateManager : function(){
40816         if (this.iframe) {
40817             return false;
40818         }
40819         return this.el.getUpdateManager();
40820     },
40821      /**
40822      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40823      * Does not work with IFRAME contents
40824      * @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:
40825 <pre><code>
40826 panel.load({
40827     url: "your-url.php",
40828     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40829     callback: yourFunction,
40830     scope: yourObject, //(optional scope)
40831     discardUrl: false,
40832     nocache: false,
40833     text: "Loading...",
40834     timeout: 30,
40835     scripts: false
40836 });
40837 </code></pre>
40838      
40839      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40840      * 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.
40841      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
40842      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40843      * @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.
40844      * @return {Roo.ContentPanel} this
40845      */
40846     load : function(){
40847         
40848         if (this.iframe) {
40849             return this;
40850         }
40851         
40852         var um = this.el.getUpdateManager();
40853         um.update.apply(um, arguments);
40854         return this;
40855     },
40856
40857
40858     /**
40859      * 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.
40860      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40861      * @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)
40862      * @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)
40863      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40864      */
40865     setUrl : function(url, params, loadOnce){
40866         if (this.iframe) {
40867             this.iframeEl.dom.src = url;
40868             return false;
40869         }
40870         
40871         if(this.refreshDelegate){
40872             this.removeListener("activate", this.refreshDelegate);
40873         }
40874         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40875         this.on("activate", this.refreshDelegate);
40876         return this.el.getUpdateManager();
40877     },
40878     
40879     _handleRefresh : function(url, params, loadOnce){
40880         if(!loadOnce || !this.loaded){
40881             var updater = this.el.getUpdateManager();
40882             updater.update(url, params, this._setLoaded.createDelegate(this));
40883         }
40884     },
40885     
40886     _setLoaded : function(){
40887         this.loaded = true;
40888     }, 
40889     
40890     /**
40891      * Returns this panel's id
40892      * @return {String} 
40893      */
40894     getId : function(){
40895         return this.el.id;
40896     },
40897     
40898     /** 
40899      * Returns this panel's element - used by regiosn to add.
40900      * @return {Roo.Element} 
40901      */
40902     getEl : function(){
40903         return this.wrapEl || this.el;
40904     },
40905     
40906    
40907     
40908     adjustForComponents : function(width, height)
40909     {
40910         //Roo.log('adjustForComponents ');
40911         if(this.resizeEl != this.el){
40912             width -= this.el.getFrameWidth('lr');
40913             height -= this.el.getFrameWidth('tb');
40914         }
40915         if(this.toolbar){
40916             var te = this.toolbar.getEl();
40917             te.setWidth(width);
40918             height -= te.getHeight();
40919         }
40920         if(this.footer){
40921             var te = this.footer.getEl();
40922             te.setWidth(width);
40923             height -= te.getHeight();
40924         }
40925         
40926         
40927         if(this.adjustments){
40928             width += this.adjustments[0];
40929             height += this.adjustments[1];
40930         }
40931         return {"width": width, "height": height};
40932     },
40933     
40934     setSize : function(width, height){
40935         if(this.fitToFrame && !this.ignoreResize(width, height)){
40936             if(this.fitContainer && this.resizeEl != this.el){
40937                 this.el.setSize(width, height);
40938             }
40939             var size = this.adjustForComponents(width, height);
40940             if (this.iframe) {
40941                 this.iframeEl.setSize(width,height);
40942             }
40943             
40944             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40945             this.fireEvent('resize', this, size.width, size.height);
40946             
40947             
40948         }
40949     },
40950     
40951     /**
40952      * Returns this panel's title
40953      * @return {String} 
40954      */
40955     getTitle : function(){
40956         
40957         if (typeof(this.title) != 'object') {
40958             return this.title;
40959         }
40960         
40961         var t = '';
40962         for (var k in this.title) {
40963             if (!this.title.hasOwnProperty(k)) {
40964                 continue;
40965             }
40966             
40967             if (k.indexOf('-') >= 0) {
40968                 var s = k.split('-');
40969                 for (var i = 0; i<s.length; i++) {
40970                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40971                 }
40972             } else {
40973                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40974             }
40975         }
40976         return t;
40977     },
40978     
40979     /**
40980      * Set this panel's title
40981      * @param {String} title
40982      */
40983     setTitle : function(title){
40984         this.title = title;
40985         if(this.region){
40986             this.region.updatePanelTitle(this, title);
40987         }
40988     },
40989     
40990     /**
40991      * Returns true is this panel was configured to be closable
40992      * @return {Boolean} 
40993      */
40994     isClosable : function(){
40995         return this.closable;
40996     },
40997     
40998     beforeSlide : function(){
40999         this.el.clip();
41000         this.resizeEl.clip();
41001     },
41002     
41003     afterSlide : function(){
41004         this.el.unclip();
41005         this.resizeEl.unclip();
41006     },
41007     
41008     /**
41009      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41010      *   Will fail silently if the {@link #setUrl} method has not been called.
41011      *   This does not activate the panel, just updates its content.
41012      */
41013     refresh : function(){
41014         if(this.refreshDelegate){
41015            this.loaded = false;
41016            this.refreshDelegate();
41017         }
41018     },
41019     
41020     /**
41021      * Destroys this panel
41022      */
41023     destroy : function(){
41024         this.el.removeAllListeners();
41025         var tempEl = document.createElement("span");
41026         tempEl.appendChild(this.el.dom);
41027         tempEl.innerHTML = "";
41028         this.el.remove();
41029         this.el = null;
41030     },
41031     
41032     /**
41033      * form - if the content panel contains a form - this is a reference to it.
41034      * @type {Roo.form.Form}
41035      */
41036     form : false,
41037     /**
41038      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41039      *    This contains a reference to it.
41040      * @type {Roo.View}
41041      */
41042     view : false,
41043     
41044       /**
41045      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41046      * <pre><code>
41047
41048 layout.addxtype({
41049        xtype : 'Form',
41050        items: [ .... ]
41051    }
41052 );
41053
41054 </code></pre>
41055      * @param {Object} cfg Xtype definition of item to add.
41056      */
41057     
41058     
41059     getChildContainer: function () {
41060         return this.getEl();
41061     },
41062     
41063     
41064     onScroll : function(e)
41065     {
41066         this.fireEvent('scroll', this, e);
41067     }
41068     
41069     
41070     /*
41071         var  ret = new Roo.factory(cfg);
41072         return ret;
41073         
41074         
41075         // add form..
41076         if (cfg.xtype.match(/^Form$/)) {
41077             
41078             var el;
41079             //if (this.footer) {
41080             //    el = this.footer.container.insertSibling(false, 'before');
41081             //} else {
41082                 el = this.el.createChild();
41083             //}
41084
41085             this.form = new  Roo.form.Form(cfg);
41086             
41087             
41088             if ( this.form.allItems.length) {
41089                 this.form.render(el.dom);
41090             }
41091             return this.form;
41092         }
41093         // should only have one of theses..
41094         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41095             // views.. should not be just added - used named prop 'view''
41096             
41097             cfg.el = this.el.appendChild(document.createElement("div"));
41098             // factory?
41099             
41100             var ret = new Roo.factory(cfg);
41101              
41102              ret.render && ret.render(false, ''); // render blank..
41103             this.view = ret;
41104             return ret;
41105         }
41106         return false;
41107     }
41108     \*/
41109 });
41110  
41111 /**
41112  * @class Roo.bootstrap.panel.Grid
41113  * @extends Roo.bootstrap.panel.Content
41114  * @constructor
41115  * Create a new GridPanel.
41116  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41117  * @param {Object} config A the config object
41118   
41119  */
41120
41121
41122
41123 Roo.bootstrap.panel.Grid = function(config)
41124 {
41125     
41126       
41127     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41128         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41129
41130     config.el = this.wrapper;
41131     //this.el = this.wrapper;
41132     
41133       if (config.container) {
41134         // ctor'ed from a Border/panel.grid
41135         
41136         
41137         this.wrapper.setStyle("overflow", "hidden");
41138         this.wrapper.addClass('roo-grid-container');
41139
41140     }
41141     
41142     
41143     if(config.toolbar){
41144         var tool_el = this.wrapper.createChild();    
41145         this.toolbar = Roo.factory(config.toolbar);
41146         var ti = [];
41147         if (config.toolbar.items) {
41148             ti = config.toolbar.items ;
41149             delete config.toolbar.items ;
41150         }
41151         
41152         var nitems = [];
41153         this.toolbar.render(tool_el);
41154         for(var i =0;i < ti.length;i++) {
41155           //  Roo.log(['add child', items[i]]);
41156             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41157         }
41158         this.toolbar.items = nitems;
41159         
41160         delete config.toolbar;
41161     }
41162     
41163     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41164     config.grid.scrollBody = true;;
41165     config.grid.monitorWindowResize = false; // turn off autosizing
41166     config.grid.autoHeight = false;
41167     config.grid.autoWidth = false;
41168     
41169     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41170     
41171     if (config.background) {
41172         // render grid on panel activation (if panel background)
41173         this.on('activate', function(gp) {
41174             if (!gp.grid.rendered) {
41175                 gp.grid.render(this.wrapper);
41176                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41177             }
41178         });
41179             
41180     } else {
41181         this.grid.render(this.wrapper);
41182         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41183
41184     }
41185     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41186     // ??? needed ??? config.el = this.wrapper;
41187     
41188     
41189     
41190   
41191     // xtype created footer. - not sure if will work as we normally have to render first..
41192     if (this.footer && !this.footer.el && this.footer.xtype) {
41193         
41194         var ctr = this.grid.getView().getFooterPanel(true);
41195         this.footer.dataSource = this.grid.dataSource;
41196         this.footer = Roo.factory(this.footer, Roo);
41197         this.footer.render(ctr);
41198         
41199     }
41200     
41201     
41202     
41203     
41204      
41205 };
41206
41207 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41208     getId : function(){
41209         return this.grid.id;
41210     },
41211     
41212     /**
41213      * Returns the grid for this panel
41214      * @return {Roo.bootstrap.Table} 
41215      */
41216     getGrid : function(){
41217         return this.grid;    
41218     },
41219     
41220     setSize : function(width, height){
41221         if(!this.ignoreResize(width, height)){
41222             var grid = this.grid;
41223             var size = this.adjustForComponents(width, height);
41224             // tfoot is not a footer?
41225           
41226             
41227             var gridel = grid.getGridEl();
41228             gridel.setSize(size.width, size.height);
41229             
41230             var tbd = grid.getGridEl().select('tbody', true).first();
41231             var thd = grid.getGridEl().select('thead',true).first();
41232             var tbf= grid.getGridEl().select('tfoot', true).first();
41233
41234             if (tbf) {
41235                 size.height -= tbf.getHeight();
41236             }
41237             if (thd) {
41238                 size.height -= thd.getHeight();
41239             }
41240             
41241             tbd.setSize(size.width, size.height );
41242             // this is for the account management tab -seems to work there.
41243             var thd = grid.getGridEl().select('thead',true).first();
41244             //if (tbd) {
41245             //    tbd.setSize(size.width, size.height - thd.getHeight());
41246             //}
41247              
41248             grid.autoSize();
41249         }
41250     },
41251      
41252     
41253     
41254     beforeSlide : function(){
41255         this.grid.getView().scroller.clip();
41256     },
41257     
41258     afterSlide : function(){
41259         this.grid.getView().scroller.unclip();
41260     },
41261     
41262     destroy : function(){
41263         this.grid.destroy();
41264         delete this.grid;
41265         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41266     }
41267 });
41268
41269 /**
41270  * @class Roo.bootstrap.panel.Nest
41271  * @extends Roo.bootstrap.panel.Content
41272  * @constructor
41273  * Create a new Panel, that can contain a layout.Border.
41274  * 
41275  * 
41276  * @param {Roo.BorderLayout} layout The layout for this panel
41277  * @param {String/Object} config A string to set only the title or a config object
41278  */
41279 Roo.bootstrap.panel.Nest = function(config)
41280 {
41281     // construct with only one argument..
41282     /* FIXME - implement nicer consturctors
41283     if (layout.layout) {
41284         config = layout;
41285         layout = config.layout;
41286         delete config.layout;
41287     }
41288     if (layout.xtype && !layout.getEl) {
41289         // then layout needs constructing..
41290         layout = Roo.factory(layout, Roo);
41291     }
41292     */
41293     
41294     config.el =  config.layout.getEl();
41295     
41296     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41297     
41298     config.layout.monitorWindowResize = false; // turn off autosizing
41299     this.layout = config.layout;
41300     this.layout.getEl().addClass("roo-layout-nested-layout");
41301     this.layout.parent = this;
41302     
41303     
41304     
41305     
41306 };
41307
41308 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41309
41310     setSize : function(width, height){
41311         if(!this.ignoreResize(width, height)){
41312             var size = this.adjustForComponents(width, height);
41313             var el = this.layout.getEl();
41314             if (size.height < 1) {
41315                 el.setWidth(size.width);   
41316             } else {
41317                 el.setSize(size.width, size.height);
41318             }
41319             var touch = el.dom.offsetWidth;
41320             this.layout.layout();
41321             // ie requires a double layout on the first pass
41322             if(Roo.isIE && !this.initialized){
41323                 this.initialized = true;
41324                 this.layout.layout();
41325             }
41326         }
41327     },
41328     
41329     // activate all subpanels if not currently active..
41330     
41331     setActiveState : function(active){
41332         this.active = active;
41333         this.setActiveClass(active);
41334         
41335         if(!active){
41336             this.fireEvent("deactivate", this);
41337             return;
41338         }
41339         
41340         this.fireEvent("activate", this);
41341         // not sure if this should happen before or after..
41342         if (!this.layout) {
41343             return; // should not happen..
41344         }
41345         var reg = false;
41346         for (var r in this.layout.regions) {
41347             reg = this.layout.getRegion(r);
41348             if (reg.getActivePanel()) {
41349                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41350                 reg.setActivePanel(reg.getActivePanel());
41351                 continue;
41352             }
41353             if (!reg.panels.length) {
41354                 continue;
41355             }
41356             reg.showPanel(reg.getPanel(0));
41357         }
41358         
41359         
41360         
41361         
41362     },
41363     
41364     /**
41365      * Returns the nested BorderLayout for this panel
41366      * @return {Roo.BorderLayout} 
41367      */
41368     getLayout : function(){
41369         return this.layout;
41370     },
41371     
41372      /**
41373      * Adds a xtype elements to the layout of the nested panel
41374      * <pre><code>
41375
41376 panel.addxtype({
41377        xtype : 'ContentPanel',
41378        region: 'west',
41379        items: [ .... ]
41380    }
41381 );
41382
41383 panel.addxtype({
41384         xtype : 'NestedLayoutPanel',
41385         region: 'west',
41386         layout: {
41387            center: { },
41388            west: { }   
41389         },
41390         items : [ ... list of content panels or nested layout panels.. ]
41391    }
41392 );
41393 </code></pre>
41394      * @param {Object} cfg Xtype definition of item to add.
41395      */
41396     addxtype : function(cfg) {
41397         return this.layout.addxtype(cfg);
41398     
41399     }
41400 });/*
41401  * Based on:
41402  * Ext JS Library 1.1.1
41403  * Copyright(c) 2006-2007, Ext JS, LLC.
41404  *
41405  * Originally Released Under LGPL - original licence link has changed is not relivant.
41406  *
41407  * Fork - LGPL
41408  * <script type="text/javascript">
41409  */
41410 /**
41411  * @class Roo.TabPanel
41412  * @extends Roo.util.Observable
41413  * A lightweight tab container.
41414  * <br><br>
41415  * Usage:
41416  * <pre><code>
41417 // basic tabs 1, built from existing content
41418 var tabs = new Roo.TabPanel("tabs1");
41419 tabs.addTab("script", "View Script");
41420 tabs.addTab("markup", "View Markup");
41421 tabs.activate("script");
41422
41423 // more advanced tabs, built from javascript
41424 var jtabs = new Roo.TabPanel("jtabs");
41425 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41426
41427 // set up the UpdateManager
41428 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41429 var updater = tab2.getUpdateManager();
41430 updater.setDefaultUrl("ajax1.htm");
41431 tab2.on('activate', updater.refresh, updater, true);
41432
41433 // Use setUrl for Ajax loading
41434 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41435 tab3.setUrl("ajax2.htm", null, true);
41436
41437 // Disabled tab
41438 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41439 tab4.disable();
41440
41441 jtabs.activate("jtabs-1");
41442  * </code></pre>
41443  * @constructor
41444  * Create a new TabPanel.
41445  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41446  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41447  */
41448 Roo.bootstrap.panel.Tabs = function(config){
41449     /**
41450     * The container element for this TabPanel.
41451     * @type Roo.Element
41452     */
41453     this.el = Roo.get(config.el);
41454     delete config.el;
41455     if(config){
41456         if(typeof config == "boolean"){
41457             this.tabPosition = config ? "bottom" : "top";
41458         }else{
41459             Roo.apply(this, config);
41460         }
41461     }
41462     
41463     if(this.tabPosition == "bottom"){
41464         // if tabs are at the bottom = create the body first.
41465         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41466         this.el.addClass("roo-tabs-bottom");
41467     }
41468     // next create the tabs holders
41469     
41470     if (this.tabPosition == "west"){
41471         
41472         var reg = this.region; // fake it..
41473         while (reg) {
41474             if (!reg.mgr.parent) {
41475                 break;
41476             }
41477             reg = reg.mgr.parent.region;
41478         }
41479         Roo.log("got nest?");
41480         Roo.log(reg);
41481         if (reg.mgr.getRegion('west')) {
41482             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41483             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41484             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41485             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41486             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41487         
41488             
41489         }
41490         
41491         
41492     } else {
41493      
41494         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41495         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41496         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41497         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41498     }
41499     
41500     
41501     if(Roo.isIE){
41502         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41503     }
41504     
41505     // finally - if tabs are at the top, then create the body last..
41506     if(this.tabPosition != "bottom"){
41507         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41508          * @type Roo.Element
41509          */
41510         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41511         this.el.addClass("roo-tabs-top");
41512     }
41513     this.items = [];
41514
41515     this.bodyEl.setStyle("position", "relative");
41516
41517     this.active = null;
41518     this.activateDelegate = this.activate.createDelegate(this);
41519
41520     this.addEvents({
41521         /**
41522          * @event tabchange
41523          * Fires when the active tab changes
41524          * @param {Roo.TabPanel} this
41525          * @param {Roo.TabPanelItem} activePanel The new active tab
41526          */
41527         "tabchange": true,
41528         /**
41529          * @event beforetabchange
41530          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41531          * @param {Roo.TabPanel} this
41532          * @param {Object} e Set cancel to true on this object to cancel the tab change
41533          * @param {Roo.TabPanelItem} tab The tab being changed to
41534          */
41535         "beforetabchange" : true
41536     });
41537
41538     Roo.EventManager.onWindowResize(this.onResize, this);
41539     this.cpad = this.el.getPadding("lr");
41540     this.hiddenCount = 0;
41541
41542
41543     // toolbar on the tabbar support...
41544     if (this.toolbar) {
41545         alert("no toolbar support yet");
41546         this.toolbar  = false;
41547         /*
41548         var tcfg = this.toolbar;
41549         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41550         this.toolbar = new Roo.Toolbar(tcfg);
41551         if (Roo.isSafari) {
41552             var tbl = tcfg.container.child('table', true);
41553             tbl.setAttribute('width', '100%');
41554         }
41555         */
41556         
41557     }
41558    
41559
41560
41561     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41562 };
41563
41564 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41565     /*
41566      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41567      */
41568     tabPosition : "top",
41569     /*
41570      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41571      */
41572     currentTabWidth : 0,
41573     /*
41574      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41575      */
41576     minTabWidth : 40,
41577     /*
41578      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41579      */
41580     maxTabWidth : 250,
41581     /*
41582      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41583      */
41584     preferredTabWidth : 175,
41585     /*
41586      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41587      */
41588     resizeTabs : false,
41589     /*
41590      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41591      */
41592     monitorResize : true,
41593     /*
41594      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41595      */
41596     toolbar : false,  // set by caller..
41597     
41598     region : false, /// set by caller
41599     
41600     disableTooltips : true, // not used yet...
41601
41602     /**
41603      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41604      * @param {String} id The id of the div to use <b>or create</b>
41605      * @param {String} text The text for the tab
41606      * @param {String} content (optional) Content to put in the TabPanelItem body
41607      * @param {Boolean} closable (optional) True to create a close icon on the tab
41608      * @return {Roo.TabPanelItem} The created TabPanelItem
41609      */
41610     addTab : function(id, text, content, closable, tpl)
41611     {
41612         var item = new Roo.bootstrap.panel.TabItem({
41613             panel: this,
41614             id : id,
41615             text : text,
41616             closable : closable,
41617             tpl : tpl
41618         });
41619         this.addTabItem(item);
41620         if(content){
41621             item.setContent(content);
41622         }
41623         return item;
41624     },
41625
41626     /**
41627      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41628      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41629      * @return {Roo.TabPanelItem}
41630      */
41631     getTab : function(id){
41632         return this.items[id];
41633     },
41634
41635     /**
41636      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41637      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41638      */
41639     hideTab : function(id){
41640         var t = this.items[id];
41641         if(!t.isHidden()){
41642            t.setHidden(true);
41643            this.hiddenCount++;
41644            this.autoSizeTabs();
41645         }
41646     },
41647
41648     /**
41649      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41650      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41651      */
41652     unhideTab : function(id){
41653         var t = this.items[id];
41654         if(t.isHidden()){
41655            t.setHidden(false);
41656            this.hiddenCount--;
41657            this.autoSizeTabs();
41658         }
41659     },
41660
41661     /**
41662      * Adds an existing {@link Roo.TabPanelItem}.
41663      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41664      */
41665     addTabItem : function(item)
41666     {
41667         this.items[item.id] = item;
41668         this.items.push(item);
41669         this.autoSizeTabs();
41670       //  if(this.resizeTabs){
41671     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41672   //         this.autoSizeTabs();
41673 //        }else{
41674 //            item.autoSize();
41675        // }
41676     },
41677
41678     /**
41679      * Removes a {@link Roo.TabPanelItem}.
41680      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41681      */
41682     removeTab : function(id){
41683         var items = this.items;
41684         var tab = items[id];
41685         if(!tab) { return; }
41686         var index = items.indexOf(tab);
41687         if(this.active == tab && items.length > 1){
41688             var newTab = this.getNextAvailable(index);
41689             if(newTab) {
41690                 newTab.activate();
41691             }
41692         }
41693         this.stripEl.dom.removeChild(tab.pnode.dom);
41694         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41695             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41696         }
41697         items.splice(index, 1);
41698         delete this.items[tab.id];
41699         tab.fireEvent("close", tab);
41700         tab.purgeListeners();
41701         this.autoSizeTabs();
41702     },
41703
41704     getNextAvailable : function(start){
41705         var items = this.items;
41706         var index = start;
41707         // look for a next tab that will slide over to
41708         // replace the one being removed
41709         while(index < items.length){
41710             var item = items[++index];
41711             if(item && !item.isHidden()){
41712                 return item;
41713             }
41714         }
41715         // if one isn't found select the previous tab (on the left)
41716         index = start;
41717         while(index >= 0){
41718             var item = items[--index];
41719             if(item && !item.isHidden()){
41720                 return item;
41721             }
41722         }
41723         return null;
41724     },
41725
41726     /**
41727      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41728      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41729      */
41730     disableTab : function(id){
41731         var tab = this.items[id];
41732         if(tab && this.active != tab){
41733             tab.disable();
41734         }
41735     },
41736
41737     /**
41738      * Enables a {@link Roo.TabPanelItem} that is disabled.
41739      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41740      */
41741     enableTab : function(id){
41742         var tab = this.items[id];
41743         tab.enable();
41744     },
41745
41746     /**
41747      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41748      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41749      * @return {Roo.TabPanelItem} The TabPanelItem.
41750      */
41751     activate : function(id)
41752     {
41753         //Roo.log('activite:'  + id);
41754         
41755         var tab = this.items[id];
41756         if(!tab){
41757             return null;
41758         }
41759         if(tab == this.active || tab.disabled){
41760             return tab;
41761         }
41762         var e = {};
41763         this.fireEvent("beforetabchange", this, e, tab);
41764         if(e.cancel !== true && !tab.disabled){
41765             if(this.active){
41766                 this.active.hide();
41767             }
41768             this.active = this.items[id];
41769             this.active.show();
41770             this.fireEvent("tabchange", this, this.active);
41771         }
41772         return tab;
41773     },
41774
41775     /**
41776      * Gets the active {@link Roo.TabPanelItem}.
41777      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41778      */
41779     getActiveTab : function(){
41780         return this.active;
41781     },
41782
41783     /**
41784      * Updates the tab body element to fit the height of the container element
41785      * for overflow scrolling
41786      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41787      */
41788     syncHeight : function(targetHeight){
41789         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41790         var bm = this.bodyEl.getMargins();
41791         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41792         this.bodyEl.setHeight(newHeight);
41793         return newHeight;
41794     },
41795
41796     onResize : function(){
41797         if(this.monitorResize){
41798             this.autoSizeTabs();
41799         }
41800     },
41801
41802     /**
41803      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41804      */
41805     beginUpdate : function(){
41806         this.updating = true;
41807     },
41808
41809     /**
41810      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41811      */
41812     endUpdate : function(){
41813         this.updating = false;
41814         this.autoSizeTabs();
41815     },
41816
41817     /**
41818      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41819      */
41820     autoSizeTabs : function()
41821     {
41822         var count = this.items.length;
41823         var vcount = count - this.hiddenCount;
41824         
41825         if (vcount < 2) {
41826             this.stripEl.hide();
41827         } else {
41828             this.stripEl.show();
41829         }
41830         
41831         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41832             return;
41833         }
41834         
41835         
41836         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41837         var availWidth = Math.floor(w / vcount);
41838         var b = this.stripBody;
41839         if(b.getWidth() > w){
41840             var tabs = this.items;
41841             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41842             if(availWidth < this.minTabWidth){
41843                 /*if(!this.sleft){    // incomplete scrolling code
41844                     this.createScrollButtons();
41845                 }
41846                 this.showScroll();
41847                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41848             }
41849         }else{
41850             if(this.currentTabWidth < this.preferredTabWidth){
41851                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41852             }
41853         }
41854     },
41855
41856     /**
41857      * Returns the number of tabs in this TabPanel.
41858      * @return {Number}
41859      */
41860      getCount : function(){
41861          return this.items.length;
41862      },
41863
41864     /**
41865      * Resizes all the tabs to the passed width
41866      * @param {Number} The new width
41867      */
41868     setTabWidth : function(width){
41869         this.currentTabWidth = width;
41870         for(var i = 0, len = this.items.length; i < len; i++) {
41871                 if(!this.items[i].isHidden()) {
41872                 this.items[i].setWidth(width);
41873             }
41874         }
41875     },
41876
41877     /**
41878      * Destroys this TabPanel
41879      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41880      */
41881     destroy : function(removeEl){
41882         Roo.EventManager.removeResizeListener(this.onResize, this);
41883         for(var i = 0, len = this.items.length; i < len; i++){
41884             this.items[i].purgeListeners();
41885         }
41886         if(removeEl === true){
41887             this.el.update("");
41888             this.el.remove();
41889         }
41890     },
41891     
41892     createStrip : function(container)
41893     {
41894         var strip = document.createElement("nav");
41895         strip.className = Roo.bootstrap.version == 4 ?
41896             "navbar-light bg-light" : 
41897             "navbar navbar-default"; //"x-tabs-wrap";
41898         container.appendChild(strip);
41899         return strip;
41900     },
41901     
41902     createStripList : function(strip)
41903     {
41904         // div wrapper for retard IE
41905         // returns the "tr" element.
41906         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41907         //'<div class="x-tabs-strip-wrap">'+
41908           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41909           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41910         return strip.firstChild; //.firstChild.firstChild.firstChild;
41911     },
41912     createBody : function(container)
41913     {
41914         var body = document.createElement("div");
41915         Roo.id(body, "tab-body");
41916         //Roo.fly(body).addClass("x-tabs-body");
41917         Roo.fly(body).addClass("tab-content");
41918         container.appendChild(body);
41919         return body;
41920     },
41921     createItemBody :function(bodyEl, id){
41922         var body = Roo.getDom(id);
41923         if(!body){
41924             body = document.createElement("div");
41925             body.id = id;
41926         }
41927         //Roo.fly(body).addClass("x-tabs-item-body");
41928         Roo.fly(body).addClass("tab-pane");
41929          bodyEl.insertBefore(body, bodyEl.firstChild);
41930         return body;
41931     },
41932     /** @private */
41933     createStripElements :  function(stripEl, text, closable, tpl)
41934     {
41935         var td = document.createElement("li"); // was td..
41936         td.className = 'nav-item';
41937         
41938         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41939         
41940         
41941         stripEl.appendChild(td);
41942         /*if(closable){
41943             td.className = "x-tabs-closable";
41944             if(!this.closeTpl){
41945                 this.closeTpl = new Roo.Template(
41946                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41947                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41948                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41949                 );
41950             }
41951             var el = this.closeTpl.overwrite(td, {"text": text});
41952             var close = el.getElementsByTagName("div")[0];
41953             var inner = el.getElementsByTagName("em")[0];
41954             return {"el": el, "close": close, "inner": inner};
41955         } else {
41956         */
41957         // not sure what this is..
41958 //            if(!this.tabTpl){
41959                 //this.tabTpl = new Roo.Template(
41960                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41961                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41962                 //);
41963 //                this.tabTpl = new Roo.Template(
41964 //                   '<a href="#">' +
41965 //                   '<span unselectable="on"' +
41966 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41967 //                            ' >{text}</span></a>'
41968 //                );
41969 //                
41970 //            }
41971
41972
41973             var template = tpl || this.tabTpl || false;
41974             
41975             if(!template){
41976                 template =  new Roo.Template(
41977                         Roo.bootstrap.version == 4 ? 
41978                             (
41979                                 '<a class="nav-link" href="#" unselectable="on"' +
41980                                      (this.disableTooltips ? '' : ' title="{text}"') +
41981                                      ' >{text}</a>'
41982                             ) : (
41983                                 '<a class="nav-link" href="#">' +
41984                                 '<span unselectable="on"' +
41985                                          (this.disableTooltips ? '' : ' title="{text}"') +
41986                                     ' >{text}</span></a>'
41987                             )
41988                 );
41989             }
41990             
41991             switch (typeof(template)) {
41992                 case 'object' :
41993                     break;
41994                 case 'string' :
41995                     template = new Roo.Template(template);
41996                     break;
41997                 default :
41998                     break;
41999             }
42000             
42001             var el = template.overwrite(td, {"text": text});
42002             
42003             var inner = el.getElementsByTagName("span")[0];
42004             
42005             return {"el": el, "inner": inner};
42006             
42007     }
42008         
42009     
42010 });
42011
42012 /**
42013  * @class Roo.TabPanelItem
42014  * @extends Roo.util.Observable
42015  * Represents an individual item (tab plus body) in a TabPanel.
42016  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42017  * @param {String} id The id of this TabPanelItem
42018  * @param {String} text The text for the tab of this TabPanelItem
42019  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42020  */
42021 Roo.bootstrap.panel.TabItem = function(config){
42022     /**
42023      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42024      * @type Roo.TabPanel
42025      */
42026     this.tabPanel = config.panel;
42027     /**
42028      * The id for this TabPanelItem
42029      * @type String
42030      */
42031     this.id = config.id;
42032     /** @private */
42033     this.disabled = false;
42034     /** @private */
42035     this.text = config.text;
42036     /** @private */
42037     this.loaded = false;
42038     this.closable = config.closable;
42039
42040     /**
42041      * The body element for this TabPanelItem.
42042      * @type Roo.Element
42043      */
42044     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42045     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42046     this.bodyEl.setStyle("display", "block");
42047     this.bodyEl.setStyle("zoom", "1");
42048     //this.hideAction();
42049
42050     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42051     /** @private */
42052     this.el = Roo.get(els.el);
42053     this.inner = Roo.get(els.inner, true);
42054      this.textEl = Roo.bootstrap.version == 4 ?
42055         this.el : Roo.get(this.el.dom.firstChild, true);
42056
42057     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42058     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42059
42060     
42061 //    this.el.on("mousedown", this.onTabMouseDown, this);
42062     this.el.on("click", this.onTabClick, this);
42063     /** @private */
42064     if(config.closable){
42065         var c = Roo.get(els.close, true);
42066         c.dom.title = this.closeText;
42067         c.addClassOnOver("close-over");
42068         c.on("click", this.closeClick, this);
42069      }
42070
42071     this.addEvents({
42072          /**
42073          * @event activate
42074          * Fires when this tab becomes the active tab.
42075          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42076          * @param {Roo.TabPanelItem} this
42077          */
42078         "activate": true,
42079         /**
42080          * @event beforeclose
42081          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42082          * @param {Roo.TabPanelItem} this
42083          * @param {Object} e Set cancel to true on this object to cancel the close.
42084          */
42085         "beforeclose": true,
42086         /**
42087          * @event close
42088          * Fires when this tab is closed.
42089          * @param {Roo.TabPanelItem} this
42090          */
42091          "close": true,
42092         /**
42093          * @event deactivate
42094          * Fires when this tab is no longer the active tab.
42095          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42096          * @param {Roo.TabPanelItem} this
42097          */
42098          "deactivate" : true
42099     });
42100     this.hidden = false;
42101
42102     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42103 };
42104
42105 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42106            {
42107     purgeListeners : function(){
42108        Roo.util.Observable.prototype.purgeListeners.call(this);
42109        this.el.removeAllListeners();
42110     },
42111     /**
42112      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42113      */
42114     show : function(){
42115         this.status_node.addClass("active");
42116         this.showAction();
42117         if(Roo.isOpera){
42118             this.tabPanel.stripWrap.repaint();
42119         }
42120         this.fireEvent("activate", this.tabPanel, this);
42121     },
42122
42123     /**
42124      * Returns true if this tab is the active tab.
42125      * @return {Boolean}
42126      */
42127     isActive : function(){
42128         return this.tabPanel.getActiveTab() == this;
42129     },
42130
42131     /**
42132      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42133      */
42134     hide : function(){
42135         this.status_node.removeClass("active");
42136         this.hideAction();
42137         this.fireEvent("deactivate", this.tabPanel, this);
42138     },
42139
42140     hideAction : function(){
42141         this.bodyEl.hide();
42142         this.bodyEl.setStyle("position", "absolute");
42143         this.bodyEl.setLeft("-20000px");
42144         this.bodyEl.setTop("-20000px");
42145     },
42146
42147     showAction : function(){
42148         this.bodyEl.setStyle("position", "relative");
42149         this.bodyEl.setTop("");
42150         this.bodyEl.setLeft("");
42151         this.bodyEl.show();
42152     },
42153
42154     /**
42155      * Set the tooltip for the tab.
42156      * @param {String} tooltip The tab's tooltip
42157      */
42158     setTooltip : function(text){
42159         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42160             this.textEl.dom.qtip = text;
42161             this.textEl.dom.removeAttribute('title');
42162         }else{
42163             this.textEl.dom.title = text;
42164         }
42165     },
42166
42167     onTabClick : function(e){
42168         e.preventDefault();
42169         this.tabPanel.activate(this.id);
42170     },
42171
42172     onTabMouseDown : function(e){
42173         e.preventDefault();
42174         this.tabPanel.activate(this.id);
42175     },
42176 /*
42177     getWidth : function(){
42178         return this.inner.getWidth();
42179     },
42180
42181     setWidth : function(width){
42182         var iwidth = width - this.linode.getPadding("lr");
42183         this.inner.setWidth(iwidth);
42184         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42185         this.linode.setWidth(width);
42186     },
42187 */
42188     /**
42189      * Show or hide the tab
42190      * @param {Boolean} hidden True to hide or false to show.
42191      */
42192     setHidden : function(hidden){
42193         this.hidden = hidden;
42194         this.linode.setStyle("display", hidden ? "none" : "");
42195     },
42196
42197     /**
42198      * Returns true if this tab is "hidden"
42199      * @return {Boolean}
42200      */
42201     isHidden : function(){
42202         return this.hidden;
42203     },
42204
42205     /**
42206      * Returns the text for this tab
42207      * @return {String}
42208      */
42209     getText : function(){
42210         return this.text;
42211     },
42212     /*
42213     autoSize : function(){
42214         //this.el.beginMeasure();
42215         this.textEl.setWidth(1);
42216         /*
42217          *  #2804 [new] Tabs in Roojs
42218          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42219          */
42220         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42221         //this.el.endMeasure();
42222     //},
42223
42224     /**
42225      * Sets the text for the tab (Note: this also sets the tooltip text)
42226      * @param {String} text The tab's text and tooltip
42227      */
42228     setText : function(text){
42229         this.text = text;
42230         this.textEl.update(text);
42231         this.setTooltip(text);
42232         //if(!this.tabPanel.resizeTabs){
42233         //    this.autoSize();
42234         //}
42235     },
42236     /**
42237      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42238      */
42239     activate : function(){
42240         this.tabPanel.activate(this.id);
42241     },
42242
42243     /**
42244      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42245      */
42246     disable : function(){
42247         if(this.tabPanel.active != this){
42248             this.disabled = true;
42249             this.status_node.addClass("disabled");
42250         }
42251     },
42252
42253     /**
42254      * Enables this TabPanelItem if it was previously disabled.
42255      */
42256     enable : function(){
42257         this.disabled = false;
42258         this.status_node.removeClass("disabled");
42259     },
42260
42261     /**
42262      * Sets the content for this TabPanelItem.
42263      * @param {String} content The content
42264      * @param {Boolean} loadScripts true to look for and load scripts
42265      */
42266     setContent : function(content, loadScripts){
42267         this.bodyEl.update(content, loadScripts);
42268     },
42269
42270     /**
42271      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42272      * @return {Roo.UpdateManager} The UpdateManager
42273      */
42274     getUpdateManager : function(){
42275         return this.bodyEl.getUpdateManager();
42276     },
42277
42278     /**
42279      * Set a URL to be used to load the content for this TabPanelItem.
42280      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42281      * @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)
42282      * @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)
42283      * @return {Roo.UpdateManager} The UpdateManager
42284      */
42285     setUrl : function(url, params, loadOnce){
42286         if(this.refreshDelegate){
42287             this.un('activate', this.refreshDelegate);
42288         }
42289         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42290         this.on("activate", this.refreshDelegate);
42291         return this.bodyEl.getUpdateManager();
42292     },
42293
42294     /** @private */
42295     _handleRefresh : function(url, params, loadOnce){
42296         if(!loadOnce || !this.loaded){
42297             var updater = this.bodyEl.getUpdateManager();
42298             updater.update(url, params, this._setLoaded.createDelegate(this));
42299         }
42300     },
42301
42302     /**
42303      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42304      *   Will fail silently if the setUrl method has not been called.
42305      *   This does not activate the panel, just updates its content.
42306      */
42307     refresh : function(){
42308         if(this.refreshDelegate){
42309            this.loaded = false;
42310            this.refreshDelegate();
42311         }
42312     },
42313
42314     /** @private */
42315     _setLoaded : function(){
42316         this.loaded = true;
42317     },
42318
42319     /** @private */
42320     closeClick : function(e){
42321         var o = {};
42322         e.stopEvent();
42323         this.fireEvent("beforeclose", this, o);
42324         if(o.cancel !== true){
42325             this.tabPanel.removeTab(this.id);
42326         }
42327     },
42328     /**
42329      * The text displayed in the tooltip for the close icon.
42330      * @type String
42331      */
42332     closeText : "Close this tab"
42333 });
42334 /**
42335 *    This script refer to:
42336 *    Title: International Telephone Input
42337 *    Author: Jack O'Connor
42338 *    Code version:  v12.1.12
42339 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42340 **/
42341
42342 Roo.bootstrap.PhoneInputData = function() {
42343     var d = [
42344       [
42345         "Afghanistan (‫افغانستان‬‎)",
42346         "af",
42347         "93"
42348       ],
42349       [
42350         "Albania (Shqipëri)",
42351         "al",
42352         "355"
42353       ],
42354       [
42355         "Algeria (‫الجزائر‬‎)",
42356         "dz",
42357         "213"
42358       ],
42359       [
42360         "American Samoa",
42361         "as",
42362         "1684"
42363       ],
42364       [
42365         "Andorra",
42366         "ad",
42367         "376"
42368       ],
42369       [
42370         "Angola",
42371         "ao",
42372         "244"
42373       ],
42374       [
42375         "Anguilla",
42376         "ai",
42377         "1264"
42378       ],
42379       [
42380         "Antigua and Barbuda",
42381         "ag",
42382         "1268"
42383       ],
42384       [
42385         "Argentina",
42386         "ar",
42387         "54"
42388       ],
42389       [
42390         "Armenia (Հայաստան)",
42391         "am",
42392         "374"
42393       ],
42394       [
42395         "Aruba",
42396         "aw",
42397         "297"
42398       ],
42399       [
42400         "Australia",
42401         "au",
42402         "61",
42403         0
42404       ],
42405       [
42406         "Austria (Österreich)",
42407         "at",
42408         "43"
42409       ],
42410       [
42411         "Azerbaijan (Azərbaycan)",
42412         "az",
42413         "994"
42414       ],
42415       [
42416         "Bahamas",
42417         "bs",
42418         "1242"
42419       ],
42420       [
42421         "Bahrain (‫البحرين‬‎)",
42422         "bh",
42423         "973"
42424       ],
42425       [
42426         "Bangladesh (বাংলাদেশ)",
42427         "bd",
42428         "880"
42429       ],
42430       [
42431         "Barbados",
42432         "bb",
42433         "1246"
42434       ],
42435       [
42436         "Belarus (Беларусь)",
42437         "by",
42438         "375"
42439       ],
42440       [
42441         "Belgium (België)",
42442         "be",
42443         "32"
42444       ],
42445       [
42446         "Belize",
42447         "bz",
42448         "501"
42449       ],
42450       [
42451         "Benin (Bénin)",
42452         "bj",
42453         "229"
42454       ],
42455       [
42456         "Bermuda",
42457         "bm",
42458         "1441"
42459       ],
42460       [
42461         "Bhutan (འབྲུག)",
42462         "bt",
42463         "975"
42464       ],
42465       [
42466         "Bolivia",
42467         "bo",
42468         "591"
42469       ],
42470       [
42471         "Bosnia and Herzegovina (Босна и Херцеговина)",
42472         "ba",
42473         "387"
42474       ],
42475       [
42476         "Botswana",
42477         "bw",
42478         "267"
42479       ],
42480       [
42481         "Brazil (Brasil)",
42482         "br",
42483         "55"
42484       ],
42485       [
42486         "British Indian Ocean Territory",
42487         "io",
42488         "246"
42489       ],
42490       [
42491         "British Virgin Islands",
42492         "vg",
42493         "1284"
42494       ],
42495       [
42496         "Brunei",
42497         "bn",
42498         "673"
42499       ],
42500       [
42501         "Bulgaria (България)",
42502         "bg",
42503         "359"
42504       ],
42505       [
42506         "Burkina Faso",
42507         "bf",
42508         "226"
42509       ],
42510       [
42511         "Burundi (Uburundi)",
42512         "bi",
42513         "257"
42514       ],
42515       [
42516         "Cambodia (កម្ពុជា)",
42517         "kh",
42518         "855"
42519       ],
42520       [
42521         "Cameroon (Cameroun)",
42522         "cm",
42523         "237"
42524       ],
42525       [
42526         "Canada",
42527         "ca",
42528         "1",
42529         1,
42530         ["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"]
42531       ],
42532       [
42533         "Cape Verde (Kabu Verdi)",
42534         "cv",
42535         "238"
42536       ],
42537       [
42538         "Caribbean Netherlands",
42539         "bq",
42540         "599",
42541         1
42542       ],
42543       [
42544         "Cayman Islands",
42545         "ky",
42546         "1345"
42547       ],
42548       [
42549         "Central African Republic (République centrafricaine)",
42550         "cf",
42551         "236"
42552       ],
42553       [
42554         "Chad (Tchad)",
42555         "td",
42556         "235"
42557       ],
42558       [
42559         "Chile",
42560         "cl",
42561         "56"
42562       ],
42563       [
42564         "China (中国)",
42565         "cn",
42566         "86"
42567       ],
42568       [
42569         "Christmas Island",
42570         "cx",
42571         "61",
42572         2
42573       ],
42574       [
42575         "Cocos (Keeling) Islands",
42576         "cc",
42577         "61",
42578         1
42579       ],
42580       [
42581         "Colombia",
42582         "co",
42583         "57"
42584       ],
42585       [
42586         "Comoros (‫جزر القمر‬‎)",
42587         "km",
42588         "269"
42589       ],
42590       [
42591         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42592         "cd",
42593         "243"
42594       ],
42595       [
42596         "Congo (Republic) (Congo-Brazzaville)",
42597         "cg",
42598         "242"
42599       ],
42600       [
42601         "Cook Islands",
42602         "ck",
42603         "682"
42604       ],
42605       [
42606         "Costa Rica",
42607         "cr",
42608         "506"
42609       ],
42610       [
42611         "Côte d’Ivoire",
42612         "ci",
42613         "225"
42614       ],
42615       [
42616         "Croatia (Hrvatska)",
42617         "hr",
42618         "385"
42619       ],
42620       [
42621         "Cuba",
42622         "cu",
42623         "53"
42624       ],
42625       [
42626         "Curaçao",
42627         "cw",
42628         "599",
42629         0
42630       ],
42631       [
42632         "Cyprus (Κύπρος)",
42633         "cy",
42634         "357"
42635       ],
42636       [
42637         "Czech Republic (Česká republika)",
42638         "cz",
42639         "420"
42640       ],
42641       [
42642         "Denmark (Danmark)",
42643         "dk",
42644         "45"
42645       ],
42646       [
42647         "Djibouti",
42648         "dj",
42649         "253"
42650       ],
42651       [
42652         "Dominica",
42653         "dm",
42654         "1767"
42655       ],
42656       [
42657         "Dominican Republic (República Dominicana)",
42658         "do",
42659         "1",
42660         2,
42661         ["809", "829", "849"]
42662       ],
42663       [
42664         "Ecuador",
42665         "ec",
42666         "593"
42667       ],
42668       [
42669         "Egypt (‫مصر‬‎)",
42670         "eg",
42671         "20"
42672       ],
42673       [
42674         "El Salvador",
42675         "sv",
42676         "503"
42677       ],
42678       [
42679         "Equatorial Guinea (Guinea Ecuatorial)",
42680         "gq",
42681         "240"
42682       ],
42683       [
42684         "Eritrea",
42685         "er",
42686         "291"
42687       ],
42688       [
42689         "Estonia (Eesti)",
42690         "ee",
42691         "372"
42692       ],
42693       [
42694         "Ethiopia",
42695         "et",
42696         "251"
42697       ],
42698       [
42699         "Falkland Islands (Islas Malvinas)",
42700         "fk",
42701         "500"
42702       ],
42703       [
42704         "Faroe Islands (Føroyar)",
42705         "fo",
42706         "298"
42707       ],
42708       [
42709         "Fiji",
42710         "fj",
42711         "679"
42712       ],
42713       [
42714         "Finland (Suomi)",
42715         "fi",
42716         "358",
42717         0
42718       ],
42719       [
42720         "France",
42721         "fr",
42722         "33"
42723       ],
42724       [
42725         "French Guiana (Guyane française)",
42726         "gf",
42727         "594"
42728       ],
42729       [
42730         "French Polynesia (Polynésie française)",
42731         "pf",
42732         "689"
42733       ],
42734       [
42735         "Gabon",
42736         "ga",
42737         "241"
42738       ],
42739       [
42740         "Gambia",
42741         "gm",
42742         "220"
42743       ],
42744       [
42745         "Georgia (საქართველო)",
42746         "ge",
42747         "995"
42748       ],
42749       [
42750         "Germany (Deutschland)",
42751         "de",
42752         "49"
42753       ],
42754       [
42755         "Ghana (Gaana)",
42756         "gh",
42757         "233"
42758       ],
42759       [
42760         "Gibraltar",
42761         "gi",
42762         "350"
42763       ],
42764       [
42765         "Greece (Ελλάδα)",
42766         "gr",
42767         "30"
42768       ],
42769       [
42770         "Greenland (Kalaallit Nunaat)",
42771         "gl",
42772         "299"
42773       ],
42774       [
42775         "Grenada",
42776         "gd",
42777         "1473"
42778       ],
42779       [
42780         "Guadeloupe",
42781         "gp",
42782         "590",
42783         0
42784       ],
42785       [
42786         "Guam",
42787         "gu",
42788         "1671"
42789       ],
42790       [
42791         "Guatemala",
42792         "gt",
42793         "502"
42794       ],
42795       [
42796         "Guernsey",
42797         "gg",
42798         "44",
42799         1
42800       ],
42801       [
42802         "Guinea (Guinée)",
42803         "gn",
42804         "224"
42805       ],
42806       [
42807         "Guinea-Bissau (Guiné Bissau)",
42808         "gw",
42809         "245"
42810       ],
42811       [
42812         "Guyana",
42813         "gy",
42814         "592"
42815       ],
42816       [
42817         "Haiti",
42818         "ht",
42819         "509"
42820       ],
42821       [
42822         "Honduras",
42823         "hn",
42824         "504"
42825       ],
42826       [
42827         "Hong Kong (香港)",
42828         "hk",
42829         "852"
42830       ],
42831       [
42832         "Hungary (Magyarország)",
42833         "hu",
42834         "36"
42835       ],
42836       [
42837         "Iceland (Ísland)",
42838         "is",
42839         "354"
42840       ],
42841       [
42842         "India (भारत)",
42843         "in",
42844         "91"
42845       ],
42846       [
42847         "Indonesia",
42848         "id",
42849         "62"
42850       ],
42851       [
42852         "Iran (‫ایران‬‎)",
42853         "ir",
42854         "98"
42855       ],
42856       [
42857         "Iraq (‫العراق‬‎)",
42858         "iq",
42859         "964"
42860       ],
42861       [
42862         "Ireland",
42863         "ie",
42864         "353"
42865       ],
42866       [
42867         "Isle of Man",
42868         "im",
42869         "44",
42870         2
42871       ],
42872       [
42873         "Israel (‫ישראל‬‎)",
42874         "il",
42875         "972"
42876       ],
42877       [
42878         "Italy (Italia)",
42879         "it",
42880         "39",
42881         0
42882       ],
42883       [
42884         "Jamaica",
42885         "jm",
42886         "1876"
42887       ],
42888       [
42889         "Japan (日本)",
42890         "jp",
42891         "81"
42892       ],
42893       [
42894         "Jersey",
42895         "je",
42896         "44",
42897         3
42898       ],
42899       [
42900         "Jordan (‫الأردن‬‎)",
42901         "jo",
42902         "962"
42903       ],
42904       [
42905         "Kazakhstan (Казахстан)",
42906         "kz",
42907         "7",
42908         1
42909       ],
42910       [
42911         "Kenya",
42912         "ke",
42913         "254"
42914       ],
42915       [
42916         "Kiribati",
42917         "ki",
42918         "686"
42919       ],
42920       [
42921         "Kosovo",
42922         "xk",
42923         "383"
42924       ],
42925       [
42926         "Kuwait (‫الكويت‬‎)",
42927         "kw",
42928         "965"
42929       ],
42930       [
42931         "Kyrgyzstan (Кыргызстан)",
42932         "kg",
42933         "996"
42934       ],
42935       [
42936         "Laos (ລາວ)",
42937         "la",
42938         "856"
42939       ],
42940       [
42941         "Latvia (Latvija)",
42942         "lv",
42943         "371"
42944       ],
42945       [
42946         "Lebanon (‫لبنان‬‎)",
42947         "lb",
42948         "961"
42949       ],
42950       [
42951         "Lesotho",
42952         "ls",
42953         "266"
42954       ],
42955       [
42956         "Liberia",
42957         "lr",
42958         "231"
42959       ],
42960       [
42961         "Libya (‫ليبيا‬‎)",
42962         "ly",
42963         "218"
42964       ],
42965       [
42966         "Liechtenstein",
42967         "li",
42968         "423"
42969       ],
42970       [
42971         "Lithuania (Lietuva)",
42972         "lt",
42973         "370"
42974       ],
42975       [
42976         "Luxembourg",
42977         "lu",
42978         "352"
42979       ],
42980       [
42981         "Macau (澳門)",
42982         "mo",
42983         "853"
42984       ],
42985       [
42986         "Macedonia (FYROM) (Македонија)",
42987         "mk",
42988         "389"
42989       ],
42990       [
42991         "Madagascar (Madagasikara)",
42992         "mg",
42993         "261"
42994       ],
42995       [
42996         "Malawi",
42997         "mw",
42998         "265"
42999       ],
43000       [
43001         "Malaysia",
43002         "my",
43003         "60"
43004       ],
43005       [
43006         "Maldives",
43007         "mv",
43008         "960"
43009       ],
43010       [
43011         "Mali",
43012         "ml",
43013         "223"
43014       ],
43015       [
43016         "Malta",
43017         "mt",
43018         "356"
43019       ],
43020       [
43021         "Marshall Islands",
43022         "mh",
43023         "692"
43024       ],
43025       [
43026         "Martinique",
43027         "mq",
43028         "596"
43029       ],
43030       [
43031         "Mauritania (‫موريتانيا‬‎)",
43032         "mr",
43033         "222"
43034       ],
43035       [
43036         "Mauritius (Moris)",
43037         "mu",
43038         "230"
43039       ],
43040       [
43041         "Mayotte",
43042         "yt",
43043         "262",
43044         1
43045       ],
43046       [
43047         "Mexico (México)",
43048         "mx",
43049         "52"
43050       ],
43051       [
43052         "Micronesia",
43053         "fm",
43054         "691"
43055       ],
43056       [
43057         "Moldova (Republica Moldova)",
43058         "md",
43059         "373"
43060       ],
43061       [
43062         "Monaco",
43063         "mc",
43064         "377"
43065       ],
43066       [
43067         "Mongolia (Монгол)",
43068         "mn",
43069         "976"
43070       ],
43071       [
43072         "Montenegro (Crna Gora)",
43073         "me",
43074         "382"
43075       ],
43076       [
43077         "Montserrat",
43078         "ms",
43079         "1664"
43080       ],
43081       [
43082         "Morocco (‫المغرب‬‎)",
43083         "ma",
43084         "212",
43085         0
43086       ],
43087       [
43088         "Mozambique (Moçambique)",
43089         "mz",
43090         "258"
43091       ],
43092       [
43093         "Myanmar (Burma) (မြန်မာ)",
43094         "mm",
43095         "95"
43096       ],
43097       [
43098         "Namibia (Namibië)",
43099         "na",
43100         "264"
43101       ],
43102       [
43103         "Nauru",
43104         "nr",
43105         "674"
43106       ],
43107       [
43108         "Nepal (नेपाल)",
43109         "np",
43110         "977"
43111       ],
43112       [
43113         "Netherlands (Nederland)",
43114         "nl",
43115         "31"
43116       ],
43117       [
43118         "New Caledonia (Nouvelle-Calédonie)",
43119         "nc",
43120         "687"
43121       ],
43122       [
43123         "New Zealand",
43124         "nz",
43125         "64"
43126       ],
43127       [
43128         "Nicaragua",
43129         "ni",
43130         "505"
43131       ],
43132       [
43133         "Niger (Nijar)",
43134         "ne",
43135         "227"
43136       ],
43137       [
43138         "Nigeria",
43139         "ng",
43140         "234"
43141       ],
43142       [
43143         "Niue",
43144         "nu",
43145         "683"
43146       ],
43147       [
43148         "Norfolk Island",
43149         "nf",
43150         "672"
43151       ],
43152       [
43153         "North Korea (조선 민주주의 인민 공화국)",
43154         "kp",
43155         "850"
43156       ],
43157       [
43158         "Northern Mariana Islands",
43159         "mp",
43160         "1670"
43161       ],
43162       [
43163         "Norway (Norge)",
43164         "no",
43165         "47",
43166         0
43167       ],
43168       [
43169         "Oman (‫عُمان‬‎)",
43170         "om",
43171         "968"
43172       ],
43173       [
43174         "Pakistan (‫پاکستان‬‎)",
43175         "pk",
43176         "92"
43177       ],
43178       [
43179         "Palau",
43180         "pw",
43181         "680"
43182       ],
43183       [
43184         "Palestine (‫فلسطين‬‎)",
43185         "ps",
43186         "970"
43187       ],
43188       [
43189         "Panama (Panamá)",
43190         "pa",
43191         "507"
43192       ],
43193       [
43194         "Papua New Guinea",
43195         "pg",
43196         "675"
43197       ],
43198       [
43199         "Paraguay",
43200         "py",
43201         "595"
43202       ],
43203       [
43204         "Peru (Perú)",
43205         "pe",
43206         "51"
43207       ],
43208       [
43209         "Philippines",
43210         "ph",
43211         "63"
43212       ],
43213       [
43214         "Poland (Polska)",
43215         "pl",
43216         "48"
43217       ],
43218       [
43219         "Portugal",
43220         "pt",
43221         "351"
43222       ],
43223       [
43224         "Puerto Rico",
43225         "pr",
43226         "1",
43227         3,
43228         ["787", "939"]
43229       ],
43230       [
43231         "Qatar (‫قطر‬‎)",
43232         "qa",
43233         "974"
43234       ],
43235       [
43236         "Réunion (La Réunion)",
43237         "re",
43238         "262",
43239         0
43240       ],
43241       [
43242         "Romania (România)",
43243         "ro",
43244         "40"
43245       ],
43246       [
43247         "Russia (Россия)",
43248         "ru",
43249         "7",
43250         0
43251       ],
43252       [
43253         "Rwanda",
43254         "rw",
43255         "250"
43256       ],
43257       [
43258         "Saint Barthélemy",
43259         "bl",
43260         "590",
43261         1
43262       ],
43263       [
43264         "Saint Helena",
43265         "sh",
43266         "290"
43267       ],
43268       [
43269         "Saint Kitts and Nevis",
43270         "kn",
43271         "1869"
43272       ],
43273       [
43274         "Saint Lucia",
43275         "lc",
43276         "1758"
43277       ],
43278       [
43279         "Saint Martin (Saint-Martin (partie française))",
43280         "mf",
43281         "590",
43282         2
43283       ],
43284       [
43285         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43286         "pm",
43287         "508"
43288       ],
43289       [
43290         "Saint Vincent and the Grenadines",
43291         "vc",
43292         "1784"
43293       ],
43294       [
43295         "Samoa",
43296         "ws",
43297         "685"
43298       ],
43299       [
43300         "San Marino",
43301         "sm",
43302         "378"
43303       ],
43304       [
43305         "São Tomé and Príncipe (São Tomé e Príncipe)",
43306         "st",
43307         "239"
43308       ],
43309       [
43310         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43311         "sa",
43312         "966"
43313       ],
43314       [
43315         "Senegal (Sénégal)",
43316         "sn",
43317         "221"
43318       ],
43319       [
43320         "Serbia (Србија)",
43321         "rs",
43322         "381"
43323       ],
43324       [
43325         "Seychelles",
43326         "sc",
43327         "248"
43328       ],
43329       [
43330         "Sierra Leone",
43331         "sl",
43332         "232"
43333       ],
43334       [
43335         "Singapore",
43336         "sg",
43337         "65"
43338       ],
43339       [
43340         "Sint Maarten",
43341         "sx",
43342         "1721"
43343       ],
43344       [
43345         "Slovakia (Slovensko)",
43346         "sk",
43347         "421"
43348       ],
43349       [
43350         "Slovenia (Slovenija)",
43351         "si",
43352         "386"
43353       ],
43354       [
43355         "Solomon Islands",
43356         "sb",
43357         "677"
43358       ],
43359       [
43360         "Somalia (Soomaaliya)",
43361         "so",
43362         "252"
43363       ],
43364       [
43365         "South Africa",
43366         "za",
43367         "27"
43368       ],
43369       [
43370         "South Korea (대한민국)",
43371         "kr",
43372         "82"
43373       ],
43374       [
43375         "South Sudan (‫جنوب السودان‬‎)",
43376         "ss",
43377         "211"
43378       ],
43379       [
43380         "Spain (España)",
43381         "es",
43382         "34"
43383       ],
43384       [
43385         "Sri Lanka (ශ්‍රී ලංකාව)",
43386         "lk",
43387         "94"
43388       ],
43389       [
43390         "Sudan (‫السودان‬‎)",
43391         "sd",
43392         "249"
43393       ],
43394       [
43395         "Suriname",
43396         "sr",
43397         "597"
43398       ],
43399       [
43400         "Svalbard and Jan Mayen",
43401         "sj",
43402         "47",
43403         1
43404       ],
43405       [
43406         "Swaziland",
43407         "sz",
43408         "268"
43409       ],
43410       [
43411         "Sweden (Sverige)",
43412         "se",
43413         "46"
43414       ],
43415       [
43416         "Switzerland (Schweiz)",
43417         "ch",
43418         "41"
43419       ],
43420       [
43421         "Syria (‫سوريا‬‎)",
43422         "sy",
43423         "963"
43424       ],
43425       [
43426         "Taiwan (台灣)",
43427         "tw",
43428         "886"
43429       ],
43430       [
43431         "Tajikistan",
43432         "tj",
43433         "992"
43434       ],
43435       [
43436         "Tanzania",
43437         "tz",
43438         "255"
43439       ],
43440       [
43441         "Thailand (ไทย)",
43442         "th",
43443         "66"
43444       ],
43445       [
43446         "Timor-Leste",
43447         "tl",
43448         "670"
43449       ],
43450       [
43451         "Togo",
43452         "tg",
43453         "228"
43454       ],
43455       [
43456         "Tokelau",
43457         "tk",
43458         "690"
43459       ],
43460       [
43461         "Tonga",
43462         "to",
43463         "676"
43464       ],
43465       [
43466         "Trinidad and Tobago",
43467         "tt",
43468         "1868"
43469       ],
43470       [
43471         "Tunisia (‫تونس‬‎)",
43472         "tn",
43473         "216"
43474       ],
43475       [
43476         "Turkey (Türkiye)",
43477         "tr",
43478         "90"
43479       ],
43480       [
43481         "Turkmenistan",
43482         "tm",
43483         "993"
43484       ],
43485       [
43486         "Turks and Caicos Islands",
43487         "tc",
43488         "1649"
43489       ],
43490       [
43491         "Tuvalu",
43492         "tv",
43493         "688"
43494       ],
43495       [
43496         "U.S. Virgin Islands",
43497         "vi",
43498         "1340"
43499       ],
43500       [
43501         "Uganda",
43502         "ug",
43503         "256"
43504       ],
43505       [
43506         "Ukraine (Україна)",
43507         "ua",
43508         "380"
43509       ],
43510       [
43511         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43512         "ae",
43513         "971"
43514       ],
43515       [
43516         "United Kingdom",
43517         "gb",
43518         "44",
43519         0
43520       ],
43521       [
43522         "United States",
43523         "us",
43524         "1",
43525         0
43526       ],
43527       [
43528         "Uruguay",
43529         "uy",
43530         "598"
43531       ],
43532       [
43533         "Uzbekistan (Oʻzbekiston)",
43534         "uz",
43535         "998"
43536       ],
43537       [
43538         "Vanuatu",
43539         "vu",
43540         "678"
43541       ],
43542       [
43543         "Vatican City (Città del Vaticano)",
43544         "va",
43545         "39",
43546         1
43547       ],
43548       [
43549         "Venezuela",
43550         "ve",
43551         "58"
43552       ],
43553       [
43554         "Vietnam (Việt Nam)",
43555         "vn",
43556         "84"
43557       ],
43558       [
43559         "Wallis and Futuna (Wallis-et-Futuna)",
43560         "wf",
43561         "681"
43562       ],
43563       [
43564         "Western Sahara (‫الصحراء الغربية‬‎)",
43565         "eh",
43566         "212",
43567         1
43568       ],
43569       [
43570         "Yemen (‫اليمن‬‎)",
43571         "ye",
43572         "967"
43573       ],
43574       [
43575         "Zambia",
43576         "zm",
43577         "260"
43578       ],
43579       [
43580         "Zimbabwe",
43581         "zw",
43582         "263"
43583       ],
43584       [
43585         "Åland Islands",
43586         "ax",
43587         "358",
43588         1
43589       ]
43590   ];
43591   
43592   return d;
43593 }/**
43594 *    This script refer to:
43595 *    Title: International Telephone Input
43596 *    Author: Jack O'Connor
43597 *    Code version:  v12.1.12
43598 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43599 **/
43600
43601 /**
43602  * @class Roo.bootstrap.PhoneInput
43603  * @extends Roo.bootstrap.TriggerField
43604  * An input with International dial-code selection
43605  
43606  * @cfg {String} defaultDialCode default '+852'
43607  * @cfg {Array} preferedCountries default []
43608   
43609  * @constructor
43610  * Create a new PhoneInput.
43611  * @param {Object} config Configuration options
43612  */
43613
43614 Roo.bootstrap.PhoneInput = function(config) {
43615     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43616 };
43617
43618 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43619         
43620         listWidth: undefined,
43621         
43622         selectedClass: 'active',
43623         
43624         invalidClass : "has-warning",
43625         
43626         validClass: 'has-success',
43627         
43628         allowed: '0123456789',
43629         
43630         max_length: 15,
43631         
43632         /**
43633          * @cfg {String} defaultDialCode The default dial code when initializing the input
43634          */
43635         defaultDialCode: '+852',
43636         
43637         /**
43638          * @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
43639          */
43640         preferedCountries: false,
43641         
43642         getAutoCreate : function()
43643         {
43644             var data = Roo.bootstrap.PhoneInputData();
43645             var align = this.labelAlign || this.parentLabelAlign();
43646             var id = Roo.id();
43647             
43648             this.allCountries = [];
43649             this.dialCodeMapping = [];
43650             
43651             for (var i = 0; i < data.length; i++) {
43652               var c = data[i];
43653               this.allCountries[i] = {
43654                 name: c[0],
43655                 iso2: c[1],
43656                 dialCode: c[2],
43657                 priority: c[3] || 0,
43658                 areaCodes: c[4] || null
43659               };
43660               this.dialCodeMapping[c[2]] = {
43661                   name: c[0],
43662                   iso2: c[1],
43663                   priority: c[3] || 0,
43664                   areaCodes: c[4] || null
43665               };
43666             }
43667             
43668             var cfg = {
43669                 cls: 'form-group',
43670                 cn: []
43671             };
43672             
43673             var input =  {
43674                 tag: 'input',
43675                 id : id,
43676                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43677                 maxlength: this.max_length,
43678                 cls : 'form-control tel-input',
43679                 autocomplete: 'new-password'
43680             };
43681             
43682             var hiddenInput = {
43683                 tag: 'input',
43684                 type: 'hidden',
43685                 cls: 'hidden-tel-input'
43686             };
43687             
43688             if (this.name) {
43689                 hiddenInput.name = this.name;
43690             }
43691             
43692             if (this.disabled) {
43693                 input.disabled = true;
43694             }
43695             
43696             var flag_container = {
43697                 tag: 'div',
43698                 cls: 'flag-box',
43699                 cn: [
43700                     {
43701                         tag: 'div',
43702                         cls: 'flag'
43703                     },
43704                     {
43705                         tag: 'div',
43706                         cls: 'caret'
43707                     }
43708                 ]
43709             };
43710             
43711             var box = {
43712                 tag: 'div',
43713                 cls: this.hasFeedback ? 'has-feedback' : '',
43714                 cn: [
43715                     hiddenInput,
43716                     input,
43717                     {
43718                         tag: 'input',
43719                         cls: 'dial-code-holder',
43720                         disabled: true
43721                     }
43722                 ]
43723             };
43724             
43725             var container = {
43726                 cls: 'roo-select2-container input-group',
43727                 cn: [
43728                     flag_container,
43729                     box
43730                 ]
43731             };
43732             
43733             if (this.fieldLabel.length) {
43734                 var indicator = {
43735                     tag: 'i',
43736                     tooltip: 'This field is required'
43737                 };
43738                 
43739                 var label = {
43740                     tag: 'label',
43741                     'for':  id,
43742                     cls: 'control-label',
43743                     cn: []
43744                 };
43745                 
43746                 var label_text = {
43747                     tag: 'span',
43748                     html: this.fieldLabel
43749                 };
43750                 
43751                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43752                 label.cn = [
43753                     indicator,
43754                     label_text
43755                 ];
43756                 
43757                 if(this.indicatorpos == 'right') {
43758                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43759                     label.cn = [
43760                         label_text,
43761                         indicator
43762                     ];
43763                 }
43764                 
43765                 if(align == 'left') {
43766                     container = {
43767                         tag: 'div',
43768                         cn: [
43769                             container
43770                         ]
43771                     };
43772                     
43773                     if(this.labelWidth > 12){
43774                         label.style = "width: " + this.labelWidth + 'px';
43775                     }
43776                     if(this.labelWidth < 13 && this.labelmd == 0){
43777                         this.labelmd = this.labelWidth;
43778                     }
43779                     if(this.labellg > 0){
43780                         label.cls += ' col-lg-' + this.labellg;
43781                         input.cls += ' col-lg-' + (12 - this.labellg);
43782                     }
43783                     if(this.labelmd > 0){
43784                         label.cls += ' col-md-' + this.labelmd;
43785                         container.cls += ' col-md-' + (12 - this.labelmd);
43786                     }
43787                     if(this.labelsm > 0){
43788                         label.cls += ' col-sm-' + this.labelsm;
43789                         container.cls += ' col-sm-' + (12 - this.labelsm);
43790                     }
43791                     if(this.labelxs > 0){
43792                         label.cls += ' col-xs-' + this.labelxs;
43793                         container.cls += ' col-xs-' + (12 - this.labelxs);
43794                     }
43795                 }
43796             }
43797             
43798             cfg.cn = [
43799                 label,
43800                 container
43801             ];
43802             
43803             var settings = this;
43804             
43805             ['xs','sm','md','lg'].map(function(size){
43806                 if (settings[size]) {
43807                     cfg.cls += ' col-' + size + '-' + settings[size];
43808                 }
43809             });
43810             
43811             this.store = new Roo.data.Store({
43812                 proxy : new Roo.data.MemoryProxy({}),
43813                 reader : new Roo.data.JsonReader({
43814                     fields : [
43815                         {
43816                             'name' : 'name',
43817                             'type' : 'string'
43818                         },
43819                         {
43820                             'name' : 'iso2',
43821                             'type' : 'string'
43822                         },
43823                         {
43824                             'name' : 'dialCode',
43825                             'type' : 'string'
43826                         },
43827                         {
43828                             'name' : 'priority',
43829                             'type' : 'string'
43830                         },
43831                         {
43832                             'name' : 'areaCodes',
43833                             'type' : 'string'
43834                         }
43835                     ]
43836                 })
43837             });
43838             
43839             if(!this.preferedCountries) {
43840                 this.preferedCountries = [
43841                     'hk',
43842                     'gb',
43843                     'us'
43844                 ];
43845             }
43846             
43847             var p = this.preferedCountries.reverse();
43848             
43849             if(p) {
43850                 for (var i = 0; i < p.length; i++) {
43851                     for (var j = 0; j < this.allCountries.length; j++) {
43852                         if(this.allCountries[j].iso2 == p[i]) {
43853                             var t = this.allCountries[j];
43854                             this.allCountries.splice(j,1);
43855                             this.allCountries.unshift(t);
43856                         }
43857                     } 
43858                 }
43859             }
43860             
43861             this.store.proxy.data = {
43862                 success: true,
43863                 data: this.allCountries
43864             };
43865             
43866             return cfg;
43867         },
43868         
43869         initEvents : function()
43870         {
43871             this.createList();
43872             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43873             
43874             this.indicator = this.indicatorEl();
43875             this.flag = this.flagEl();
43876             this.dialCodeHolder = this.dialCodeHolderEl();
43877             
43878             this.trigger = this.el.select('div.flag-box',true).first();
43879             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43880             
43881             var _this = this;
43882             
43883             (function(){
43884                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43885                 _this.list.setWidth(lw);
43886             }).defer(100);
43887             
43888             this.list.on('mouseover', this.onViewOver, this);
43889             this.list.on('mousemove', this.onViewMove, this);
43890             this.inputEl().on("keyup", this.onKeyUp, this);
43891             this.inputEl().on("keypress", this.onKeyPress, this);
43892             
43893             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43894
43895             this.view = new Roo.View(this.list, this.tpl, {
43896                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43897             });
43898             
43899             this.view.on('click', this.onViewClick, this);
43900             this.setValue(this.defaultDialCode);
43901         },
43902         
43903         onTriggerClick : function(e)
43904         {
43905             Roo.log('trigger click');
43906             if(this.disabled){
43907                 return;
43908             }
43909             
43910             if(this.isExpanded()){
43911                 this.collapse();
43912                 this.hasFocus = false;
43913             }else {
43914                 this.store.load({});
43915                 this.hasFocus = true;
43916                 this.expand();
43917             }
43918         },
43919         
43920         isExpanded : function()
43921         {
43922             return this.list.isVisible();
43923         },
43924         
43925         collapse : function()
43926         {
43927             if(!this.isExpanded()){
43928                 return;
43929             }
43930             this.list.hide();
43931             Roo.get(document).un('mousedown', this.collapseIf, this);
43932             Roo.get(document).un('mousewheel', this.collapseIf, this);
43933             this.fireEvent('collapse', this);
43934             this.validate();
43935         },
43936         
43937         expand : function()
43938         {
43939             Roo.log('expand');
43940
43941             if(this.isExpanded() || !this.hasFocus){
43942                 return;
43943             }
43944             
43945             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43946             this.list.setWidth(lw);
43947             
43948             this.list.show();
43949             this.restrictHeight();
43950             
43951             Roo.get(document).on('mousedown', this.collapseIf, this);
43952             Roo.get(document).on('mousewheel', this.collapseIf, this);
43953             
43954             this.fireEvent('expand', this);
43955         },
43956         
43957         restrictHeight : function()
43958         {
43959             this.list.alignTo(this.inputEl(), this.listAlign);
43960             this.list.alignTo(this.inputEl(), this.listAlign);
43961         },
43962         
43963         onViewOver : function(e, t)
43964         {
43965             if(this.inKeyMode){
43966                 return;
43967             }
43968             var item = this.view.findItemFromChild(t);
43969             
43970             if(item){
43971                 var index = this.view.indexOf(item);
43972                 this.select(index, false);
43973             }
43974         },
43975
43976         // private
43977         onViewClick : function(view, doFocus, el, e)
43978         {
43979             var index = this.view.getSelectedIndexes()[0];
43980             
43981             var r = this.store.getAt(index);
43982             
43983             if(r){
43984                 this.onSelect(r, index);
43985             }
43986             if(doFocus !== false && !this.blockFocus){
43987                 this.inputEl().focus();
43988             }
43989         },
43990         
43991         onViewMove : function(e, t)
43992         {
43993             this.inKeyMode = false;
43994         },
43995         
43996         select : function(index, scrollIntoView)
43997         {
43998             this.selectedIndex = index;
43999             this.view.select(index);
44000             if(scrollIntoView !== false){
44001                 var el = this.view.getNode(index);
44002                 if(el){
44003                     this.list.scrollChildIntoView(el, false);
44004                 }
44005             }
44006         },
44007         
44008         createList : function()
44009         {
44010             this.list = Roo.get(document.body).createChild({
44011                 tag: 'ul',
44012                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44013                 style: 'display:none'
44014             });
44015             
44016             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44017         },
44018         
44019         collapseIf : function(e)
44020         {
44021             var in_combo  = e.within(this.el);
44022             var in_list =  e.within(this.list);
44023             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44024             
44025             if (in_combo || in_list || is_list) {
44026                 return;
44027             }
44028             this.collapse();
44029         },
44030         
44031         onSelect : function(record, index)
44032         {
44033             if(this.fireEvent('beforeselect', this, record, index) !== false){
44034                 
44035                 this.setFlagClass(record.data.iso2);
44036                 this.setDialCode(record.data.dialCode);
44037                 this.hasFocus = false;
44038                 this.collapse();
44039                 this.fireEvent('select', this, record, index);
44040             }
44041         },
44042         
44043         flagEl : function()
44044         {
44045             var flag = this.el.select('div.flag',true).first();
44046             if(!flag){
44047                 return false;
44048             }
44049             return flag;
44050         },
44051         
44052         dialCodeHolderEl : function()
44053         {
44054             var d = this.el.select('input.dial-code-holder',true).first();
44055             if(!d){
44056                 return false;
44057             }
44058             return d;
44059         },
44060         
44061         setDialCode : function(v)
44062         {
44063             this.dialCodeHolder.dom.value = '+'+v;
44064         },
44065         
44066         setFlagClass : function(n)
44067         {
44068             this.flag.dom.className = 'flag '+n;
44069         },
44070         
44071         getValue : function()
44072         {
44073             var v = this.inputEl().getValue();
44074             if(this.dialCodeHolder) {
44075                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44076             }
44077             return v;
44078         },
44079         
44080         setValue : function(v)
44081         {
44082             var d = this.getDialCode(v);
44083             
44084             //invalid dial code
44085             if(v.length == 0 || !d || d.length == 0) {
44086                 if(this.rendered){
44087                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44088                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44089                 }
44090                 return;
44091             }
44092             
44093             //valid dial code
44094             this.setFlagClass(this.dialCodeMapping[d].iso2);
44095             this.setDialCode(d);
44096             this.inputEl().dom.value = v.replace('+'+d,'');
44097             this.hiddenEl().dom.value = this.getValue();
44098             
44099             this.validate();
44100         },
44101         
44102         getDialCode : function(v)
44103         {
44104             v = v ||  '';
44105             
44106             if (v.length == 0) {
44107                 return this.dialCodeHolder.dom.value;
44108             }
44109             
44110             var dialCode = "";
44111             if (v.charAt(0) != "+") {
44112                 return false;
44113             }
44114             var numericChars = "";
44115             for (var i = 1; i < v.length; i++) {
44116               var c = v.charAt(i);
44117               if (!isNaN(c)) {
44118                 numericChars += c;
44119                 if (this.dialCodeMapping[numericChars]) {
44120                   dialCode = v.substr(1, i);
44121                 }
44122                 if (numericChars.length == 4) {
44123                   break;
44124                 }
44125               }
44126             }
44127             return dialCode;
44128         },
44129         
44130         reset : function()
44131         {
44132             this.setValue(this.defaultDialCode);
44133             this.validate();
44134         },
44135         
44136         hiddenEl : function()
44137         {
44138             return this.el.select('input.hidden-tel-input',true).first();
44139         },
44140         
44141         // after setting val
44142         onKeyUp : function(e){
44143             this.setValue(this.getValue());
44144         },
44145         
44146         onKeyPress : function(e){
44147             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44148                 e.stopEvent();
44149             }
44150         }
44151         
44152 });
44153 /**
44154  * @class Roo.bootstrap.MoneyField
44155  * @extends Roo.bootstrap.ComboBox
44156  * Bootstrap MoneyField class
44157  * 
44158  * @constructor
44159  * Create a new MoneyField.
44160  * @param {Object} config Configuration options
44161  */
44162
44163 Roo.bootstrap.MoneyField = function(config) {
44164     
44165     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44166     
44167 };
44168
44169 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44170     
44171     /**
44172      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44173      */
44174     allowDecimals : true,
44175     /**
44176      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44177      */
44178     decimalSeparator : ".",
44179     /**
44180      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44181      */
44182     decimalPrecision : 0,
44183     /**
44184      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44185      */
44186     allowNegative : true,
44187     /**
44188      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44189      */
44190     allowZero: true,
44191     /**
44192      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44193      */
44194     minValue : Number.NEGATIVE_INFINITY,
44195     /**
44196      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44197      */
44198     maxValue : Number.MAX_VALUE,
44199     /**
44200      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44201      */
44202     minText : "The minimum value for this field is {0}",
44203     /**
44204      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44205      */
44206     maxText : "The maximum value for this field is {0}",
44207     /**
44208      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44209      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44210      */
44211     nanText : "{0} is not a valid number",
44212     /**
44213      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44214      */
44215     castInt : true,
44216     /**
44217      * @cfg {String} defaults currency of the MoneyField
44218      * value should be in lkey
44219      */
44220     defaultCurrency : false,
44221     /**
44222      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44223      */
44224     thousandsDelimiter : false,
44225     /**
44226      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44227      */
44228     max_length: false,
44229     
44230     inputlg : 9,
44231     inputmd : 9,
44232     inputsm : 9,
44233     inputxs : 6,
44234     
44235     store : false,
44236     
44237     getAutoCreate : function()
44238     {
44239         var align = this.labelAlign || this.parentLabelAlign();
44240         
44241         var id = Roo.id();
44242
44243         var cfg = {
44244             cls: 'form-group',
44245             cn: []
44246         };
44247
44248         var input =  {
44249             tag: 'input',
44250             id : id,
44251             cls : 'form-control roo-money-amount-input',
44252             autocomplete: 'new-password'
44253         };
44254         
44255         var hiddenInput = {
44256             tag: 'input',
44257             type: 'hidden',
44258             id: Roo.id(),
44259             cls: 'hidden-number-input'
44260         };
44261         
44262         if(this.max_length) {
44263             input.maxlength = this.max_length; 
44264         }
44265         
44266         if (this.name) {
44267             hiddenInput.name = this.name;
44268         }
44269
44270         if (this.disabled) {
44271             input.disabled = true;
44272         }
44273
44274         var clg = 12 - this.inputlg;
44275         var cmd = 12 - this.inputmd;
44276         var csm = 12 - this.inputsm;
44277         var cxs = 12 - this.inputxs;
44278         
44279         var container = {
44280             tag : 'div',
44281             cls : 'row roo-money-field',
44282             cn : [
44283                 {
44284                     tag : 'div',
44285                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44286                     cn : [
44287                         {
44288                             tag : 'div',
44289                             cls: 'roo-select2-container input-group',
44290                             cn: [
44291                                 {
44292                                     tag : 'input',
44293                                     cls : 'form-control roo-money-currency-input',
44294                                     autocomplete: 'new-password',
44295                                     readOnly : 1,
44296                                     name : this.currencyName
44297                                 },
44298                                 {
44299                                     tag :'span',
44300                                     cls : 'input-group-addon',
44301                                     cn : [
44302                                         {
44303                                             tag: 'span',
44304                                             cls: 'caret'
44305                                         }
44306                                     ]
44307                                 }
44308                             ]
44309                         }
44310                     ]
44311                 },
44312                 {
44313                     tag : 'div',
44314                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44315                     cn : [
44316                         {
44317                             tag: 'div',
44318                             cls: this.hasFeedback ? 'has-feedback' : '',
44319                             cn: [
44320                                 input
44321                             ]
44322                         }
44323                     ]
44324                 }
44325             ]
44326             
44327         };
44328         
44329         if (this.fieldLabel.length) {
44330             var indicator = {
44331                 tag: 'i',
44332                 tooltip: 'This field is required'
44333             };
44334
44335             var label = {
44336                 tag: 'label',
44337                 'for':  id,
44338                 cls: 'control-label',
44339                 cn: []
44340             };
44341
44342             var label_text = {
44343                 tag: 'span',
44344                 html: this.fieldLabel
44345             };
44346
44347             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44348             label.cn = [
44349                 indicator,
44350                 label_text
44351             ];
44352
44353             if(this.indicatorpos == 'right') {
44354                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44355                 label.cn = [
44356                     label_text,
44357                     indicator
44358                 ];
44359             }
44360
44361             if(align == 'left') {
44362                 container = {
44363                     tag: 'div',
44364                     cn: [
44365                         container
44366                     ]
44367                 };
44368
44369                 if(this.labelWidth > 12){
44370                     label.style = "width: " + this.labelWidth + 'px';
44371                 }
44372                 if(this.labelWidth < 13 && this.labelmd == 0){
44373                     this.labelmd = this.labelWidth;
44374                 }
44375                 if(this.labellg > 0){
44376                     label.cls += ' col-lg-' + this.labellg;
44377                     input.cls += ' col-lg-' + (12 - this.labellg);
44378                 }
44379                 if(this.labelmd > 0){
44380                     label.cls += ' col-md-' + this.labelmd;
44381                     container.cls += ' col-md-' + (12 - this.labelmd);
44382                 }
44383                 if(this.labelsm > 0){
44384                     label.cls += ' col-sm-' + this.labelsm;
44385                     container.cls += ' col-sm-' + (12 - this.labelsm);
44386                 }
44387                 if(this.labelxs > 0){
44388                     label.cls += ' col-xs-' + this.labelxs;
44389                     container.cls += ' col-xs-' + (12 - this.labelxs);
44390                 }
44391             }
44392         }
44393
44394         cfg.cn = [
44395             label,
44396             container,
44397             hiddenInput
44398         ];
44399         
44400         var settings = this;
44401
44402         ['xs','sm','md','lg'].map(function(size){
44403             if (settings[size]) {
44404                 cfg.cls += ' col-' + size + '-' + settings[size];
44405             }
44406         });
44407         
44408         return cfg;
44409     },
44410     
44411     initEvents : function()
44412     {
44413         this.indicator = this.indicatorEl();
44414         
44415         this.initCurrencyEvent();
44416         
44417         this.initNumberEvent();
44418     },
44419     
44420     initCurrencyEvent : function()
44421     {
44422         if (!this.store) {
44423             throw "can not find store for combo";
44424         }
44425         
44426         this.store = Roo.factory(this.store, Roo.data);
44427         this.store.parent = this;
44428         
44429         this.createList();
44430         
44431         this.triggerEl = this.el.select('.input-group-addon', true).first();
44432         
44433         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44434         
44435         var _this = this;
44436         
44437         (function(){
44438             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44439             _this.list.setWidth(lw);
44440         }).defer(100);
44441         
44442         this.list.on('mouseover', this.onViewOver, this);
44443         this.list.on('mousemove', this.onViewMove, this);
44444         this.list.on('scroll', this.onViewScroll, this);
44445         
44446         if(!this.tpl){
44447             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44448         }
44449         
44450         this.view = new Roo.View(this.list, this.tpl, {
44451             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44452         });
44453         
44454         this.view.on('click', this.onViewClick, this);
44455         
44456         this.store.on('beforeload', this.onBeforeLoad, this);
44457         this.store.on('load', this.onLoad, this);
44458         this.store.on('loadexception', this.onLoadException, this);
44459         
44460         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44461             "up" : function(e){
44462                 this.inKeyMode = true;
44463                 this.selectPrev();
44464             },
44465
44466             "down" : function(e){
44467                 if(!this.isExpanded()){
44468                     this.onTriggerClick();
44469                 }else{
44470                     this.inKeyMode = true;
44471                     this.selectNext();
44472                 }
44473             },
44474
44475             "enter" : function(e){
44476                 this.collapse();
44477                 
44478                 if(this.fireEvent("specialkey", this, e)){
44479                     this.onViewClick(false);
44480                 }
44481                 
44482                 return true;
44483             },
44484
44485             "esc" : function(e){
44486                 this.collapse();
44487             },
44488
44489             "tab" : function(e){
44490                 this.collapse();
44491                 
44492                 if(this.fireEvent("specialkey", this, e)){
44493                     this.onViewClick(false);
44494                 }
44495                 
44496                 return true;
44497             },
44498
44499             scope : this,
44500
44501             doRelay : function(foo, bar, hname){
44502                 if(hname == 'down' || this.scope.isExpanded()){
44503                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44504                 }
44505                 return true;
44506             },
44507
44508             forceKeyDown: true
44509         });
44510         
44511         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44512         
44513     },
44514     
44515     initNumberEvent : function(e)
44516     {
44517         this.inputEl().on("keydown" , this.fireKey,  this);
44518         this.inputEl().on("focus", this.onFocus,  this);
44519         this.inputEl().on("blur", this.onBlur,  this);
44520         
44521         this.inputEl().relayEvent('keyup', this);
44522         
44523         if(this.indicator){
44524             this.indicator.addClass('invisible');
44525         }
44526  
44527         this.originalValue = this.getValue();
44528         
44529         if(this.validationEvent == 'keyup'){
44530             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44531             this.inputEl().on('keyup', this.filterValidation, this);
44532         }
44533         else if(this.validationEvent !== false){
44534             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44535         }
44536         
44537         if(this.selectOnFocus){
44538             this.on("focus", this.preFocus, this);
44539             
44540         }
44541         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44542             this.inputEl().on("keypress", this.filterKeys, this);
44543         } else {
44544             this.inputEl().relayEvent('keypress', this);
44545         }
44546         
44547         var allowed = "0123456789";
44548         
44549         if(this.allowDecimals){
44550             allowed += this.decimalSeparator;
44551         }
44552         
44553         if(this.allowNegative){
44554             allowed += "-";
44555         }
44556         
44557         if(this.thousandsDelimiter) {
44558             allowed += ",";
44559         }
44560         
44561         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44562         
44563         var keyPress = function(e){
44564             
44565             var k = e.getKey();
44566             
44567             var c = e.getCharCode();
44568             
44569             if(
44570                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44571                     allowed.indexOf(String.fromCharCode(c)) === -1
44572             ){
44573                 e.stopEvent();
44574                 return;
44575             }
44576             
44577             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44578                 return;
44579             }
44580             
44581             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44582                 e.stopEvent();
44583             }
44584         };
44585         
44586         this.inputEl().on("keypress", keyPress, this);
44587         
44588     },
44589     
44590     onTriggerClick : function(e)
44591     {   
44592         if(this.disabled){
44593             return;
44594         }
44595         
44596         this.page = 0;
44597         this.loadNext = false;
44598         
44599         if(this.isExpanded()){
44600             this.collapse();
44601             return;
44602         }
44603         
44604         this.hasFocus = true;
44605         
44606         if(this.triggerAction == 'all') {
44607             this.doQuery(this.allQuery, true);
44608             return;
44609         }
44610         
44611         this.doQuery(this.getRawValue());
44612     },
44613     
44614     getCurrency : function()
44615     {   
44616         var v = this.currencyEl().getValue();
44617         
44618         return v;
44619     },
44620     
44621     restrictHeight : function()
44622     {
44623         this.list.alignTo(this.currencyEl(), this.listAlign);
44624         this.list.alignTo(this.currencyEl(), this.listAlign);
44625     },
44626     
44627     onViewClick : function(view, doFocus, el, e)
44628     {
44629         var index = this.view.getSelectedIndexes()[0];
44630         
44631         var r = this.store.getAt(index);
44632         
44633         if(r){
44634             this.onSelect(r, index);
44635         }
44636     },
44637     
44638     onSelect : function(record, index){
44639         
44640         if(this.fireEvent('beforeselect', this, record, index) !== false){
44641         
44642             this.setFromCurrencyData(index > -1 ? record.data : false);
44643             
44644             this.collapse();
44645             
44646             this.fireEvent('select', this, record, index);
44647         }
44648     },
44649     
44650     setFromCurrencyData : function(o)
44651     {
44652         var currency = '';
44653         
44654         this.lastCurrency = o;
44655         
44656         if (this.currencyField) {
44657             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44658         } else {
44659             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44660         }
44661         
44662         this.lastSelectionText = currency;
44663         
44664         //setting default currency
44665         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44666             this.setCurrency(this.defaultCurrency);
44667             return;
44668         }
44669         
44670         this.setCurrency(currency);
44671     },
44672     
44673     setFromData : function(o)
44674     {
44675         var c = {};
44676         
44677         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44678         
44679         this.setFromCurrencyData(c);
44680         
44681         var value = '';
44682         
44683         if (this.name) {
44684             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44685         } else {
44686             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44687         }
44688         
44689         this.setValue(value);
44690         
44691     },
44692     
44693     setCurrency : function(v)
44694     {   
44695         this.currencyValue = v;
44696         
44697         if(this.rendered){
44698             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44699             this.validate();
44700         }
44701     },
44702     
44703     setValue : function(v)
44704     {
44705         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44706         
44707         this.value = v;
44708         
44709         if(this.rendered){
44710             
44711             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44712             
44713             this.inputEl().dom.value = (v == '') ? '' :
44714                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44715             
44716             if(!this.allowZero && v === '0') {
44717                 this.hiddenEl().dom.value = '';
44718                 this.inputEl().dom.value = '';
44719             }
44720             
44721             this.validate();
44722         }
44723     },
44724     
44725     getRawValue : function()
44726     {
44727         var v = this.inputEl().getValue();
44728         
44729         return v;
44730     },
44731     
44732     getValue : function()
44733     {
44734         return this.fixPrecision(this.parseValue(this.getRawValue()));
44735     },
44736     
44737     parseValue : function(value)
44738     {
44739         if(this.thousandsDelimiter) {
44740             value += "";
44741             r = new RegExp(",", "g");
44742             value = value.replace(r, "");
44743         }
44744         
44745         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44746         return isNaN(value) ? '' : value;
44747         
44748     },
44749     
44750     fixPrecision : function(value)
44751     {
44752         if(this.thousandsDelimiter) {
44753             value += "";
44754             r = new RegExp(",", "g");
44755             value = value.replace(r, "");
44756         }
44757         
44758         var nan = isNaN(value);
44759         
44760         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44761             return nan ? '' : value;
44762         }
44763         return parseFloat(value).toFixed(this.decimalPrecision);
44764     },
44765     
44766     decimalPrecisionFcn : function(v)
44767     {
44768         return Math.floor(v);
44769     },
44770     
44771     validateValue : function(value)
44772     {
44773         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44774             return false;
44775         }
44776         
44777         var num = this.parseValue(value);
44778         
44779         if(isNaN(num)){
44780             this.markInvalid(String.format(this.nanText, value));
44781             return false;
44782         }
44783         
44784         if(num < this.minValue){
44785             this.markInvalid(String.format(this.minText, this.minValue));
44786             return false;
44787         }
44788         
44789         if(num > this.maxValue){
44790             this.markInvalid(String.format(this.maxText, this.maxValue));
44791             return false;
44792         }
44793         
44794         return true;
44795     },
44796     
44797     validate : function()
44798     {
44799         if(this.disabled || this.allowBlank){
44800             this.markValid();
44801             return true;
44802         }
44803         
44804         var currency = this.getCurrency();
44805         
44806         if(this.validateValue(this.getRawValue()) && currency.length){
44807             this.markValid();
44808             return true;
44809         }
44810         
44811         this.markInvalid();
44812         return false;
44813     },
44814     
44815     getName: function()
44816     {
44817         return this.name;
44818     },
44819     
44820     beforeBlur : function()
44821     {
44822         if(!this.castInt){
44823             return;
44824         }
44825         
44826         var v = this.parseValue(this.getRawValue());
44827         
44828         if(v || v == 0){
44829             this.setValue(v);
44830         }
44831     },
44832     
44833     onBlur : function()
44834     {
44835         this.beforeBlur();
44836         
44837         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44838             //this.el.removeClass(this.focusClass);
44839         }
44840         
44841         this.hasFocus = false;
44842         
44843         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44844             this.validate();
44845         }
44846         
44847         var v = this.getValue();
44848         
44849         if(String(v) !== String(this.startValue)){
44850             this.fireEvent('change', this, v, this.startValue);
44851         }
44852         
44853         this.fireEvent("blur", this);
44854     },
44855     
44856     inputEl : function()
44857     {
44858         return this.el.select('.roo-money-amount-input', true).first();
44859     },
44860     
44861     currencyEl : function()
44862     {
44863         return this.el.select('.roo-money-currency-input', true).first();
44864     },
44865     
44866     hiddenEl : function()
44867     {
44868         return this.el.select('input.hidden-number-input',true).first();
44869     }
44870     
44871 });/**
44872  * @class Roo.bootstrap.BezierSignature
44873  * @extends Roo.bootstrap.Component
44874  * Bootstrap BezierSignature class
44875  * This script refer to:
44876  *    Title: Signature Pad
44877  *    Author: szimek
44878  *    Availability: https://github.com/szimek/signature_pad
44879  *
44880  * @constructor
44881  * Create a new BezierSignature
44882  * @param {Object} config The config object
44883  */
44884
44885 Roo.bootstrap.BezierSignature = function(config){
44886     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44887     this.addEvents({
44888         "resize" : true
44889     });
44890 };
44891
44892 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44893 {
44894      
44895     curve_data: [],
44896     
44897     is_empty: true,
44898     
44899     mouse_btn_down: true,
44900     
44901     /**
44902      * @cfg {int} canvas height
44903      */
44904     canvas_height: '200px',
44905     
44906     /**
44907      * @cfg {float|function} Radius of a single dot.
44908      */ 
44909     dot_size: false,
44910     
44911     /**
44912      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44913      */
44914     min_width: 0.5,
44915     
44916     /**
44917      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44918      */
44919     max_width: 2.5,
44920     
44921     /**
44922      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44923      */
44924     throttle: 16,
44925     
44926     /**
44927      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44928      */
44929     min_distance: 5,
44930     
44931     /**
44932      * @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.
44933      */
44934     bg_color: 'rgba(0, 0, 0, 0)',
44935     
44936     /**
44937      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44938      */
44939     dot_color: 'black',
44940     
44941     /**
44942      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44943      */ 
44944     velocity_filter_weight: 0.7,
44945     
44946     /**
44947      * @cfg {function} Callback when stroke begin. 
44948      */
44949     onBegin: false,
44950     
44951     /**
44952      * @cfg {function} Callback when stroke end.
44953      */
44954     onEnd: false,
44955     
44956     getAutoCreate : function()
44957     {
44958         var cls = 'roo-signature column';
44959         
44960         if(this.cls){
44961             cls += ' ' + this.cls;
44962         }
44963         
44964         var col_sizes = [
44965             'lg',
44966             'md',
44967             'sm',
44968             'xs'
44969         ];
44970         
44971         for(var i = 0; i < col_sizes.length; i++) {
44972             if(this[col_sizes[i]]) {
44973                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44974             }
44975         }
44976         
44977         var cfg = {
44978             tag: 'div',
44979             cls: cls,
44980             cn: [
44981                 {
44982                     tag: 'div',
44983                     cls: 'roo-signature-body',
44984                     cn: [
44985                         {
44986                             tag: 'canvas',
44987                             cls: 'roo-signature-body-canvas',
44988                             height: this.canvas_height,
44989                             width: this.canvas_width
44990                         }
44991                     ]
44992                 },
44993                 {
44994                     tag: 'input',
44995                     type: 'file',
44996                     style: 'display: none'
44997                 }
44998             ]
44999         };
45000         
45001         return cfg;
45002     },
45003     
45004     initEvents: function() 
45005     {
45006         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45007         
45008         var canvas = this.canvasEl();
45009         
45010         // mouse && touch event swapping...
45011         canvas.dom.style.touchAction = 'none';
45012         canvas.dom.style.msTouchAction = 'none';
45013         
45014         this.mouse_btn_down = false;
45015         canvas.on('mousedown', this._handleMouseDown, this);
45016         canvas.on('mousemove', this._handleMouseMove, this);
45017         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45018         
45019         if (window.PointerEvent) {
45020             canvas.on('pointerdown', this._handleMouseDown, this);
45021             canvas.on('pointermove', this._handleMouseMove, this);
45022             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45023         }
45024         
45025         if ('ontouchstart' in window) {
45026             canvas.on('touchstart', this._handleTouchStart, this);
45027             canvas.on('touchmove', this._handleTouchMove, this);
45028             canvas.on('touchend', this._handleTouchEnd, this);
45029         }
45030         
45031         Roo.EventManager.onWindowResize(this.resize, this, true);
45032         
45033         // file input event
45034         this.fileEl().on('change', this.uploadImage, this);
45035         
45036         this.clear();
45037         
45038         this.resize();
45039     },
45040     
45041     resize: function(){
45042         
45043         var canvas = this.canvasEl().dom;
45044         var ctx = this.canvasElCtx();
45045         var img_data = false;
45046         
45047         if(canvas.width > 0) {
45048             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45049         }
45050         // setting canvas width will clean img data
45051         canvas.width = 0;
45052         
45053         var style = window.getComputedStyle ? 
45054             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45055             
45056         var padding_left = parseInt(style.paddingLeft) || 0;
45057         var padding_right = parseInt(style.paddingRight) || 0;
45058         
45059         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45060         
45061         if(img_data) {
45062             ctx.putImageData(img_data, 0, 0);
45063         }
45064     },
45065     
45066     _handleMouseDown: function(e)
45067     {
45068         if (e.browserEvent.which === 1) {
45069             this.mouse_btn_down = true;
45070             this.strokeBegin(e);
45071         }
45072     },
45073     
45074     _handleMouseMove: function (e)
45075     {
45076         if (this.mouse_btn_down) {
45077             this.strokeMoveUpdate(e);
45078         }
45079     },
45080     
45081     _handleMouseUp: function (e)
45082     {
45083         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45084             this.mouse_btn_down = false;
45085             this.strokeEnd(e);
45086         }
45087     },
45088     
45089     _handleTouchStart: function (e) {
45090         
45091         e.preventDefault();
45092         if (e.browserEvent.targetTouches.length === 1) {
45093             // var touch = e.browserEvent.changedTouches[0];
45094             // this.strokeBegin(touch);
45095             
45096              this.strokeBegin(e); // assume e catching the correct xy...
45097         }
45098     },
45099     
45100     _handleTouchMove: function (e) {
45101         e.preventDefault();
45102         // var touch = event.targetTouches[0];
45103         // _this._strokeMoveUpdate(touch);
45104         this.strokeMoveUpdate(e);
45105     },
45106     
45107     _handleTouchEnd: function (e) {
45108         var wasCanvasTouched = e.target === this.canvasEl().dom;
45109         if (wasCanvasTouched) {
45110             e.preventDefault();
45111             // var touch = event.changedTouches[0];
45112             // _this._strokeEnd(touch);
45113             this.strokeEnd(e);
45114         }
45115     },
45116     
45117     reset: function () {
45118         this._lastPoints = [];
45119         this._lastVelocity = 0;
45120         this._lastWidth = (this.min_width + this.max_width) / 2;
45121         this.canvasElCtx().fillStyle = this.dot_color;
45122     },
45123     
45124     strokeMoveUpdate: function(e)
45125     {
45126         this.strokeUpdate(e);
45127         
45128         if (this.throttle) {
45129             this.throttleStroke(this.strokeUpdate, this.throttle);
45130         }
45131         else {
45132             this.strokeUpdate(e);
45133         }
45134     },
45135     
45136     strokeBegin: function(e)
45137     {
45138         var newPointGroup = {
45139             color: this.dot_color,
45140             points: []
45141         };
45142         
45143         if (typeof this.onBegin === 'function') {
45144             this.onBegin(e);
45145         }
45146         
45147         this.curve_data.push(newPointGroup);
45148         this.reset();
45149         this.strokeUpdate(e);
45150     },
45151     
45152     strokeUpdate: function(e)
45153     {
45154         var rect = this.canvasEl().dom.getBoundingClientRect();
45155         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45156         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45157         var lastPoints = lastPointGroup.points;
45158         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45159         var isLastPointTooClose = lastPoint
45160             ? point.distanceTo(lastPoint) <= this.min_distance
45161             : false;
45162         var color = lastPointGroup.color;
45163         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45164             var curve = this.addPoint(point);
45165             if (!lastPoint) {
45166                 this.drawDot({color: color, point: point});
45167             }
45168             else if (curve) {
45169                 this.drawCurve({color: color, curve: curve});
45170             }
45171             lastPoints.push({
45172                 time: point.time,
45173                 x: point.x,
45174                 y: point.y
45175             });
45176         }
45177     },
45178     
45179     strokeEnd: function(e)
45180     {
45181         this.strokeUpdate(e);
45182         if (typeof this.onEnd === 'function') {
45183             this.onEnd(e);
45184         }
45185     },
45186     
45187     addPoint:  function (point) {
45188         var _lastPoints = this._lastPoints;
45189         _lastPoints.push(point);
45190         if (_lastPoints.length > 2) {
45191             if (_lastPoints.length === 3) {
45192                 _lastPoints.unshift(_lastPoints[0]);
45193             }
45194             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45195             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45196             _lastPoints.shift();
45197             return curve;
45198         }
45199         return null;
45200     },
45201     
45202     calculateCurveWidths: function (startPoint, endPoint) {
45203         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45204             (1 - this.velocity_filter_weight) * this._lastVelocity;
45205
45206         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45207         var widths = {
45208             end: newWidth,
45209             start: this._lastWidth
45210         };
45211         
45212         this._lastVelocity = velocity;
45213         this._lastWidth = newWidth;
45214         return widths;
45215     },
45216     
45217     drawDot: function (_a) {
45218         var color = _a.color, point = _a.point;
45219         var ctx = this.canvasElCtx();
45220         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45221         ctx.beginPath();
45222         this.drawCurveSegment(point.x, point.y, width);
45223         ctx.closePath();
45224         ctx.fillStyle = color;
45225         ctx.fill();
45226     },
45227     
45228     drawCurve: function (_a) {
45229         var color = _a.color, curve = _a.curve;
45230         var ctx = this.canvasElCtx();
45231         var widthDelta = curve.endWidth - curve.startWidth;
45232         var drawSteps = Math.floor(curve.length()) * 2;
45233         ctx.beginPath();
45234         ctx.fillStyle = color;
45235         for (var i = 0; i < drawSteps; i += 1) {
45236         var t = i / drawSteps;
45237         var tt = t * t;
45238         var ttt = tt * t;
45239         var u = 1 - t;
45240         var uu = u * u;
45241         var uuu = uu * u;
45242         var x = uuu * curve.startPoint.x;
45243         x += 3 * uu * t * curve.control1.x;
45244         x += 3 * u * tt * curve.control2.x;
45245         x += ttt * curve.endPoint.x;
45246         var y = uuu * curve.startPoint.y;
45247         y += 3 * uu * t * curve.control1.y;
45248         y += 3 * u * tt * curve.control2.y;
45249         y += ttt * curve.endPoint.y;
45250         var width = curve.startWidth + ttt * widthDelta;
45251         this.drawCurveSegment(x, y, width);
45252         }
45253         ctx.closePath();
45254         ctx.fill();
45255     },
45256     
45257     drawCurveSegment: function (x, y, width) {
45258         var ctx = this.canvasElCtx();
45259         ctx.moveTo(x, y);
45260         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45261         this.is_empty = false;
45262     },
45263     
45264     clear: function()
45265     {
45266         var ctx = this.canvasElCtx();
45267         var canvas = this.canvasEl().dom;
45268         ctx.fillStyle = this.bg_color;
45269         ctx.clearRect(0, 0, canvas.width, canvas.height);
45270         ctx.fillRect(0, 0, canvas.width, canvas.height);
45271         this.curve_data = [];
45272         this.reset();
45273         this.is_empty = true;
45274     },
45275     
45276     fileEl: function()
45277     {
45278         return  this.el.select('input',true).first();
45279     },
45280     
45281     canvasEl: function()
45282     {
45283         return this.el.select('canvas',true).first();
45284     },
45285     
45286     canvasElCtx: function()
45287     {
45288         return this.el.select('canvas',true).first().dom.getContext('2d');
45289     },
45290     
45291     getImage: function(type)
45292     {
45293         if(this.is_empty) {
45294             return false;
45295         }
45296         
45297         // encryption ?
45298         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45299     },
45300     
45301     drawFromImage: function(img_src)
45302     {
45303         var img = new Image();
45304         
45305         img.onload = function(){
45306             this.canvasElCtx().drawImage(img, 0, 0);
45307         }.bind(this);
45308         
45309         img.src = img_src;
45310         
45311         this.is_empty = false;
45312     },
45313     
45314     selectImage: function()
45315     {
45316         this.fileEl().dom.click();
45317     },
45318     
45319     uploadImage: function(e)
45320     {
45321         var reader = new FileReader();
45322         
45323         reader.onload = function(e){
45324             var img = new Image();
45325             img.onload = function(){
45326                 this.reset();
45327                 this.canvasElCtx().drawImage(img, 0, 0);
45328             }.bind(this);
45329             img.src = e.target.result;
45330         }.bind(this);
45331         
45332         reader.readAsDataURL(e.target.files[0]);
45333     },
45334     
45335     // Bezier Point Constructor
45336     Point: (function () {
45337         function Point(x, y, time) {
45338             this.x = x;
45339             this.y = y;
45340             this.time = time || Date.now();
45341         }
45342         Point.prototype.distanceTo = function (start) {
45343             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45344         };
45345         Point.prototype.equals = function (other) {
45346             return this.x === other.x && this.y === other.y && this.time === other.time;
45347         };
45348         Point.prototype.velocityFrom = function (start) {
45349             return this.time !== start.time
45350             ? this.distanceTo(start) / (this.time - start.time)
45351             : 0;
45352         };
45353         return Point;
45354     }()),
45355     
45356     
45357     // Bezier Constructor
45358     Bezier: (function () {
45359         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45360             this.startPoint = startPoint;
45361             this.control2 = control2;
45362             this.control1 = control1;
45363             this.endPoint = endPoint;
45364             this.startWidth = startWidth;
45365             this.endWidth = endWidth;
45366         }
45367         Bezier.fromPoints = function (points, widths, scope) {
45368             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45369             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45370             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45371         };
45372         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45373             var dx1 = s1.x - s2.x;
45374             var dy1 = s1.y - s2.y;
45375             var dx2 = s2.x - s3.x;
45376             var dy2 = s2.y - s3.y;
45377             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45378             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45379             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45380             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45381             var dxm = m1.x - m2.x;
45382             var dym = m1.y - m2.y;
45383             var k = l2 / (l1 + l2);
45384             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45385             var tx = s2.x - cm.x;
45386             var ty = s2.y - cm.y;
45387             return {
45388                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45389                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45390             };
45391         };
45392         Bezier.prototype.length = function () {
45393             var steps = 10;
45394             var length = 0;
45395             var px;
45396             var py;
45397             for (var i = 0; i <= steps; i += 1) {
45398                 var t = i / steps;
45399                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45400                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45401                 if (i > 0) {
45402                     var xdiff = cx - px;
45403                     var ydiff = cy - py;
45404                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45405                 }
45406                 px = cx;
45407                 py = cy;
45408             }
45409             return length;
45410         };
45411         Bezier.prototype.point = function (t, start, c1, c2, end) {
45412             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45413             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45414             + (3.0 * c2 * (1.0 - t) * t * t)
45415             + (end * t * t * t);
45416         };
45417         return Bezier;
45418     }()),
45419     
45420     throttleStroke: function(fn, wait) {
45421       if (wait === void 0) { wait = 250; }
45422       var previous = 0;
45423       var timeout = null;
45424       var result;
45425       var storedContext;
45426       var storedArgs;
45427       var later = function () {
45428           previous = Date.now();
45429           timeout = null;
45430           result = fn.apply(storedContext, storedArgs);
45431           if (!timeout) {
45432               storedContext = null;
45433               storedArgs = [];
45434           }
45435       };
45436       return function wrapper() {
45437           var args = [];
45438           for (var _i = 0; _i < arguments.length; _i++) {
45439               args[_i] = arguments[_i];
45440           }
45441           var now = Date.now();
45442           var remaining = wait - (now - previous);
45443           storedContext = this;
45444           storedArgs = args;
45445           if (remaining <= 0 || remaining > wait) {
45446               if (timeout) {
45447                   clearTimeout(timeout);
45448                   timeout = null;
45449               }
45450               previous = now;
45451               result = fn.apply(storedContext, storedArgs);
45452               if (!timeout) {
45453                   storedContext = null;
45454                   storedArgs = [];
45455               }
45456           }
45457           else if (!timeout) {
45458               timeout = window.setTimeout(later, remaining);
45459           }
45460           return result;
45461       };
45462   }
45463   
45464 });
45465
45466  
45467
45468