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 = 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     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <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>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
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  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     alignEl : false, // when show is called with an element - this get's stored.
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         this.arrowEl = this.el.select('.arrow',true).first();
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792  
19793         
19794         if (this.over === false && !this.parent()) {
19795             return; 
19796         }
19797         if (this.triggers === false) {
19798             return;
19799         }
19800          
19801         // support parent
19802         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19803         var triggers = this.trigger ? this.trigger.split(' ') : [];
19804         Roo.each(triggers, function(trigger) {
19805         
19806             if (trigger == 'click') {
19807                 on_el.on('click', this.toggle, this);
19808             } else if (trigger != 'manual') {
19809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19811       
19812                 on_el.on(eventIn  ,this.enter, this);
19813                 on_el.on(eventOut, this.leave, this);
19814             }
19815         }, this);
19816     },
19817     
19818     
19819     // private
19820     timeout : null,
19821     hoverState : null,
19822     
19823     toggle : function () {
19824         this.hoverState == 'in' ? this.leave() : this.enter();
19825     },
19826     
19827     enter : function () {
19828         
19829         clearTimeout(this.timeout);
19830     
19831         this.hoverState = 'in';
19832     
19833         if (!this.delay || !this.delay.show) {
19834             this.show();
19835             return;
19836         }
19837         var _t = this;
19838         this.timeout = setTimeout(function () {
19839             if (_t.hoverState == 'in') {
19840                 _t.show();
19841             }
19842         }, this.delay.show)
19843     },
19844     
19845     leave : function() {
19846         clearTimeout(this.timeout);
19847     
19848         this.hoverState = 'out';
19849     
19850         if (!this.delay || !this.delay.hide) {
19851             this.hide();
19852             return;
19853         }
19854         var _t = this;
19855         this.timeout = setTimeout(function () {
19856             if (_t.hoverState == 'out') {
19857                 _t.hide();
19858             }
19859         }, this.delay.hide)
19860     },
19861     /**
19862      * Show the popover
19863      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19864      * @param {string} (left|right|top|bottom) position
19865      */
19866     show : function (on_el, placement)
19867     {
19868         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19869         on_el = on_el || false; // default to false
19870          
19871         if (!on_el) {
19872             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19873                 on_el = this.parent().el;
19874             } else if (this.over) {
19875                 Roo.get(this.over);
19876             }
19877             
19878         }
19879         
19880         if (!this.el) {
19881             this.render(document.body);
19882         }
19883         
19884         
19885         this.el.removeClass([
19886             'fade','top','bottom', 'left', 'right','in',
19887             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19888         ]);
19889         
19890         if (this.title === false) {
19891             this.headerEl.hide();
19892         }
19893         
19894        
19895         this.el.show();
19896         this.el.dom.style.display = 'block';
19897          
19898         
19899         this.el.addClass(placement + ' roo-popover-' + placement);
19900
19901         if (on_el) {
19902             this.updatePosition();
19903              
19904         } else {
19905             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19906             var es = this.el.getSize();
19907             var x = Roo.lib.Dom.getViewWidth()/2;
19908             var y = Roo.lib.Dom.getViewHeight()/2;
19909             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19910             
19911         }
19912
19913         
19914         //var arrow = this.el.select('.arrow',true).first();
19915         //arrow.set(align[2], 
19916         
19917         this.el.addClass('in');
19918         
19919          
19920         
19921         this.hoverState = 'in';
19922         
19923         if (this.modal) {
19924             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19925             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19926             this.maskEl.dom.style.display = 'block';
19927             this.maskEl.addClass('show');
19928         }
19929         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19930  
19931         this.fireEvent('show', this);
19932         
19933     },
19934     /**
19935      * fire this manually after loading a grid in the table for example
19936      * @param {string} (left|right|top|bottom) where to try and put it
19937      * @param {Boolean} try and move it if we cant get right position.
19938      */
19939     updatePosition : function(placement, try_move)
19940     {
19941         this.el.removeClass([
19942             'fade','top','bottom', 'left', 'right','in',
19943             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19944         ]);
19945         this.el.addClass(placement + ' roo-popover-' + placement);
19946         
19947         if (!this.alignEl ) {
19948             return false;
19949         }
19950         
19951         switch (placement) {
19952             case 'right':
19953                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19954                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19955                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19956                     //normal display... or moved up/down.
19957                     this.setXY(offset);
19958                     var xy = this.alignEl.getAnchorXY('tr', false);
19959                     xy[0]+=2;xy[1]+=5;
19960                     this.arrowEl.setXY(xy);
19961                     return true;
19962                 }
19963                 // continue through...
19964                 try_move = false;
19965                 
19966             
19967             case 'left':
19968                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19969                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19970                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19971                     //normal display... or moved up/down.
19972                     this.setXY(offset);
19973                     var xy = this.alignEl.getAnchorXY('tl', false);
19974                     xy[0]+=2;xy[1]+=5; // << fix me
19975                     this.arrowEl.setXY(xy);
19976                     return true;
19977                 }
19978                 // call self...
19979                 return this.updatePosition('right', false);
19980             
19981             case 'top':
19982                 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,-10]);
19983                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,-10]);
19984                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19985                     //normal display... or moved up/down.
19986                     this.setXY(offset);
19987                     var xy = this.alignEl.getAnchorXY('b', false);
19988                     xy[0]+=2;xy[1]+=5; // << fix me
19989                     this.arrowEl.setXY(xy);
19990                     return true;
19991                 }
19992                 // fall through
19993                 try_move = false;
19994             
19995             case 'bottom':
19996                  var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,10]);
19997                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,10]);
19998                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
19999                     //normal display... or moved up/down.
20000                     this.setXY(offset);
20001                     var xy = this.alignEl.getAnchorXY('t', false);
20002                     xy[0]+=2;xy[1]+=5; // << fix me
20003                     this.arrowEl.setXY(xy);
20004                     return true;
20005                 }
20006                 // fall through
20007                 return this.updatePosition('top', false);
20008                 
20009             
20010         }
20011         
20012         
20013         return false;
20014     },
20015     
20016     hide : function()
20017     {
20018         this.el.setXY([0,0]);
20019         this.el.removeClass('in');
20020         this.el.hide();
20021         this.hoverState = null;
20022         this.maskEl.hide(); // always..
20023         this.fireEvent('hide', this);
20024     }
20025     
20026 });
20027
20028
20029 Roo.apply(Roo.bootstrap.Popover, {
20030
20031     alignment : {
20032         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20033         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20034         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20035         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20036     },
20037     
20038     zIndex : 20001,
20039
20040     clickHander : false,
20041     
20042
20043     onMouseDown : function(e)
20044     {
20045         if (!e.getTarget(".roo-popover")) {
20046             this.hideAll();
20047         }
20048          
20049     },
20050     
20051     popups : [],
20052     
20053     register : function(popup)
20054     {
20055         if (!Roo.bootstrap.Popover.clickHandler) {
20056             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20057         }
20058         // hide other popups.
20059         this.hideAll();
20060         this.popups.push(popup);
20061     },
20062     hideAll : function()
20063     {
20064         this.popups.forEach(function(p) {
20065             p.hide();
20066         });
20067     }
20068
20069 });/*
20070  * - LGPL
20071  *
20072  * Card header - holder for the card header elements.
20073  * 
20074  */
20075
20076 /**
20077  * @class Roo.bootstrap.PopoverNav
20078  * @extends Roo.bootstrap.NavGroup
20079  * Bootstrap Popover header navigation class
20080  * @constructor
20081  * Create a new Popover Header Navigation 
20082  * @param {Object} config The config object
20083  */
20084
20085 Roo.bootstrap.PopoverNav = function(config){
20086     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20087 };
20088
20089 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20090     
20091     
20092     container_method : 'getPopoverHeader' 
20093     
20094      
20095     
20096     
20097    
20098 });
20099
20100  
20101
20102  /*
20103  * - LGPL
20104  *
20105  * Progress
20106  * 
20107  */
20108
20109 /**
20110  * @class Roo.bootstrap.Progress
20111  * @extends Roo.bootstrap.Component
20112  * Bootstrap Progress class
20113  * @cfg {Boolean} striped striped of the progress bar
20114  * @cfg {Boolean} active animated of the progress bar
20115  * 
20116  * 
20117  * @constructor
20118  * Create a new Progress
20119  * @param {Object} config The config object
20120  */
20121
20122 Roo.bootstrap.Progress = function(config){
20123     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20124 };
20125
20126 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20127     
20128     striped : false,
20129     active: false,
20130     
20131     getAutoCreate : function(){
20132         var cfg = {
20133             tag: 'div',
20134             cls: 'progress'
20135         };
20136         
20137         
20138         if(this.striped){
20139             cfg.cls += ' progress-striped';
20140         }
20141       
20142         if(this.active){
20143             cfg.cls += ' active';
20144         }
20145         
20146         
20147         return cfg;
20148     }
20149    
20150 });
20151
20152  
20153
20154  /*
20155  * - LGPL
20156  *
20157  * ProgressBar
20158  * 
20159  */
20160
20161 /**
20162  * @class Roo.bootstrap.ProgressBar
20163  * @extends Roo.bootstrap.Component
20164  * Bootstrap ProgressBar class
20165  * @cfg {Number} aria_valuenow aria-value now
20166  * @cfg {Number} aria_valuemin aria-value min
20167  * @cfg {Number} aria_valuemax aria-value max
20168  * @cfg {String} label label for the progress bar
20169  * @cfg {String} panel (success | info | warning | danger )
20170  * @cfg {String} role role of the progress bar
20171  * @cfg {String} sr_only text
20172  * 
20173  * 
20174  * @constructor
20175  * Create a new ProgressBar
20176  * @param {Object} config The config object
20177  */
20178
20179 Roo.bootstrap.ProgressBar = function(config){
20180     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20181 };
20182
20183 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20184     
20185     aria_valuenow : 0,
20186     aria_valuemin : 0,
20187     aria_valuemax : 100,
20188     label : false,
20189     panel : false,
20190     role : false,
20191     sr_only: false,
20192     
20193     getAutoCreate : function()
20194     {
20195         
20196         var cfg = {
20197             tag: 'div',
20198             cls: 'progress-bar',
20199             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20200         };
20201         
20202         if(this.sr_only){
20203             cfg.cn = {
20204                 tag: 'span',
20205                 cls: 'sr-only',
20206                 html: this.sr_only
20207             }
20208         }
20209         
20210         if(this.role){
20211             cfg.role = this.role;
20212         }
20213         
20214         if(this.aria_valuenow){
20215             cfg['aria-valuenow'] = this.aria_valuenow;
20216         }
20217         
20218         if(this.aria_valuemin){
20219             cfg['aria-valuemin'] = this.aria_valuemin;
20220         }
20221         
20222         if(this.aria_valuemax){
20223             cfg['aria-valuemax'] = this.aria_valuemax;
20224         }
20225         
20226         if(this.label && !this.sr_only){
20227             cfg.html = this.label;
20228         }
20229         
20230         if(this.panel){
20231             cfg.cls += ' progress-bar-' + this.panel;
20232         }
20233         
20234         return cfg;
20235     },
20236     
20237     update : function(aria_valuenow)
20238     {
20239         this.aria_valuenow = aria_valuenow;
20240         
20241         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20242     }
20243    
20244 });
20245
20246  
20247
20248  /*
20249  * - LGPL
20250  *
20251  * column
20252  * 
20253  */
20254
20255 /**
20256  * @class Roo.bootstrap.TabGroup
20257  * @extends Roo.bootstrap.Column
20258  * Bootstrap Column class
20259  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20260  * @cfg {Boolean} carousel true to make the group behave like a carousel
20261  * @cfg {Boolean} bullets show bullets for the panels
20262  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20263  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20264  * @cfg {Boolean} showarrow (true|false) show arrow default true
20265  * 
20266  * @constructor
20267  * Create a new TabGroup
20268  * @param {Object} config The config object
20269  */
20270
20271 Roo.bootstrap.TabGroup = function(config){
20272     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20273     if (!this.navId) {
20274         this.navId = Roo.id();
20275     }
20276     this.tabs = [];
20277     Roo.bootstrap.TabGroup.register(this);
20278     
20279 };
20280
20281 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20282     
20283     carousel : false,
20284     transition : false,
20285     bullets : 0,
20286     timer : 0,
20287     autoslide : false,
20288     slideFn : false,
20289     slideOnTouch : false,
20290     showarrow : true,
20291     
20292     getAutoCreate : function()
20293     {
20294         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20295         
20296         cfg.cls += ' tab-content';
20297         
20298         if (this.carousel) {
20299             cfg.cls += ' carousel slide';
20300             
20301             cfg.cn = [{
20302                cls : 'carousel-inner',
20303                cn : []
20304             }];
20305         
20306             if(this.bullets  && !Roo.isTouch){
20307                 
20308                 var bullets = {
20309                     cls : 'carousel-bullets',
20310                     cn : []
20311                 };
20312                
20313                 if(this.bullets_cls){
20314                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20315                 }
20316                 
20317                 bullets.cn.push({
20318                     cls : 'clear'
20319                 });
20320                 
20321                 cfg.cn[0].cn.push(bullets);
20322             }
20323             
20324             if(this.showarrow){
20325                 cfg.cn[0].cn.push({
20326                     tag : 'div',
20327                     class : 'carousel-arrow',
20328                     cn : [
20329                         {
20330                             tag : 'div',
20331                             class : 'carousel-prev',
20332                             cn : [
20333                                 {
20334                                     tag : 'i',
20335                                     class : 'fa fa-chevron-left'
20336                                 }
20337                             ]
20338                         },
20339                         {
20340                             tag : 'div',
20341                             class : 'carousel-next',
20342                             cn : [
20343                                 {
20344                                     tag : 'i',
20345                                     class : 'fa fa-chevron-right'
20346                                 }
20347                             ]
20348                         }
20349                     ]
20350                 });
20351             }
20352             
20353         }
20354         
20355         return cfg;
20356     },
20357     
20358     initEvents:  function()
20359     {
20360 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20361 //            this.el.on("touchstart", this.onTouchStart, this);
20362 //        }
20363         
20364         if(this.autoslide){
20365             var _this = this;
20366             
20367             this.slideFn = window.setInterval(function() {
20368                 _this.showPanelNext();
20369             }, this.timer);
20370         }
20371         
20372         if(this.showarrow){
20373             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20374             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20375         }
20376         
20377         
20378     },
20379     
20380 //    onTouchStart : function(e, el, o)
20381 //    {
20382 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20383 //            return;
20384 //        }
20385 //        
20386 //        this.showPanelNext();
20387 //    },
20388     
20389     
20390     getChildContainer : function()
20391     {
20392         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20393     },
20394     
20395     /**
20396     * register a Navigation item
20397     * @param {Roo.bootstrap.NavItem} the navitem to add
20398     */
20399     register : function(item)
20400     {
20401         this.tabs.push( item);
20402         item.navId = this.navId; // not really needed..
20403         this.addBullet();
20404     
20405     },
20406     
20407     getActivePanel : function()
20408     {
20409         var r = false;
20410         Roo.each(this.tabs, function(t) {
20411             if (t.active) {
20412                 r = t;
20413                 return false;
20414             }
20415             return null;
20416         });
20417         return r;
20418         
20419     },
20420     getPanelByName : function(n)
20421     {
20422         var r = false;
20423         Roo.each(this.tabs, function(t) {
20424             if (t.tabId == n) {
20425                 r = t;
20426                 return false;
20427             }
20428             return null;
20429         });
20430         return r;
20431     },
20432     indexOfPanel : function(p)
20433     {
20434         var r = false;
20435         Roo.each(this.tabs, function(t,i) {
20436             if (t.tabId == p.tabId) {
20437                 r = i;
20438                 return false;
20439             }
20440             return null;
20441         });
20442         return r;
20443     },
20444     /**
20445      * show a specific panel
20446      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20447      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20448      */
20449     showPanel : function (pan)
20450     {
20451         if(this.transition || typeof(pan) == 'undefined'){
20452             Roo.log("waiting for the transitionend");
20453             return false;
20454         }
20455         
20456         if (typeof(pan) == 'number') {
20457             pan = this.tabs[pan];
20458         }
20459         
20460         if (typeof(pan) == 'string') {
20461             pan = this.getPanelByName(pan);
20462         }
20463         
20464         var cur = this.getActivePanel();
20465         
20466         if(!pan || !cur){
20467             Roo.log('pan or acitve pan is undefined');
20468             return false;
20469         }
20470         
20471         if (pan.tabId == this.getActivePanel().tabId) {
20472             return true;
20473         }
20474         
20475         if (false === cur.fireEvent('beforedeactivate')) {
20476             return false;
20477         }
20478         
20479         if(this.bullets > 0 && !Roo.isTouch){
20480             this.setActiveBullet(this.indexOfPanel(pan));
20481         }
20482         
20483         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20484             
20485             //class="carousel-item carousel-item-next carousel-item-left"
20486             
20487             this.transition = true;
20488             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20489             var lr = dir == 'next' ? 'left' : 'right';
20490             pan.el.addClass(dir); // or prev
20491             pan.el.addClass('carousel-item-' + dir); // or prev
20492             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20493             cur.el.addClass(lr); // or right
20494             pan.el.addClass(lr);
20495             cur.el.addClass('carousel-item-' +lr); // or right
20496             pan.el.addClass('carousel-item-' +lr);
20497             
20498             
20499             var _this = this;
20500             cur.el.on('transitionend', function() {
20501                 Roo.log("trans end?");
20502                 
20503                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20504                 pan.setActive(true);
20505                 
20506                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20507                 cur.setActive(false);
20508                 
20509                 _this.transition = false;
20510                 
20511             }, this, { single:  true } );
20512             
20513             return true;
20514         }
20515         
20516         cur.setActive(false);
20517         pan.setActive(true);
20518         
20519         return true;
20520         
20521     },
20522     showPanelNext : function()
20523     {
20524         var i = this.indexOfPanel(this.getActivePanel());
20525         
20526         if (i >= this.tabs.length - 1 && !this.autoslide) {
20527             return;
20528         }
20529         
20530         if (i >= this.tabs.length - 1 && this.autoslide) {
20531             i = -1;
20532         }
20533         
20534         this.showPanel(this.tabs[i+1]);
20535     },
20536     
20537     showPanelPrev : function()
20538     {
20539         var i = this.indexOfPanel(this.getActivePanel());
20540         
20541         if (i  < 1 && !this.autoslide) {
20542             return;
20543         }
20544         
20545         if (i < 1 && this.autoslide) {
20546             i = this.tabs.length;
20547         }
20548         
20549         this.showPanel(this.tabs[i-1]);
20550     },
20551     
20552     
20553     addBullet: function()
20554     {
20555         if(!this.bullets || Roo.isTouch){
20556             return;
20557         }
20558         var ctr = this.el.select('.carousel-bullets',true).first();
20559         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20560         var bullet = ctr.createChild({
20561             cls : 'bullet bullet-' + i
20562         },ctr.dom.lastChild);
20563         
20564         
20565         var _this = this;
20566         
20567         bullet.on('click', (function(e, el, o, ii, t){
20568
20569             e.preventDefault();
20570
20571             this.showPanel(ii);
20572
20573             if(this.autoslide && this.slideFn){
20574                 clearInterval(this.slideFn);
20575                 this.slideFn = window.setInterval(function() {
20576                     _this.showPanelNext();
20577                 }, this.timer);
20578             }
20579
20580         }).createDelegate(this, [i, bullet], true));
20581                 
20582         
20583     },
20584      
20585     setActiveBullet : function(i)
20586     {
20587         if(Roo.isTouch){
20588             return;
20589         }
20590         
20591         Roo.each(this.el.select('.bullet', true).elements, function(el){
20592             el.removeClass('selected');
20593         });
20594
20595         var bullet = this.el.select('.bullet-' + i, true).first();
20596         
20597         if(!bullet){
20598             return;
20599         }
20600         
20601         bullet.addClass('selected');
20602     }
20603     
20604     
20605   
20606 });
20607
20608  
20609
20610  
20611  
20612 Roo.apply(Roo.bootstrap.TabGroup, {
20613     
20614     groups: {},
20615      /**
20616     * register a Navigation Group
20617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20618     */
20619     register : function(navgrp)
20620     {
20621         this.groups[navgrp.navId] = navgrp;
20622         
20623     },
20624     /**
20625     * fetch a Navigation Group based on the navigation ID
20626     * if one does not exist , it will get created.
20627     * @param {string} the navgroup to add
20628     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20629     */
20630     get: function(navId) {
20631         if (typeof(this.groups[navId]) == 'undefined') {
20632             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20633         }
20634         return this.groups[navId] ;
20635     }
20636     
20637     
20638     
20639 });
20640
20641  /*
20642  * - LGPL
20643  *
20644  * TabPanel
20645  * 
20646  */
20647
20648 /**
20649  * @class Roo.bootstrap.TabPanel
20650  * @extends Roo.bootstrap.Component
20651  * Bootstrap TabPanel class
20652  * @cfg {Boolean} active panel active
20653  * @cfg {String} html panel content
20654  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20655  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20656  * @cfg {String} href click to link..
20657  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20658  * 
20659  * 
20660  * @constructor
20661  * Create a new TabPanel
20662  * @param {Object} config The config object
20663  */
20664
20665 Roo.bootstrap.TabPanel = function(config){
20666     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20667     this.addEvents({
20668         /**
20669              * @event changed
20670              * Fires when the active status changes
20671              * @param {Roo.bootstrap.TabPanel} this
20672              * @param {Boolean} state the new state
20673             
20674          */
20675         'changed': true,
20676         /**
20677              * @event beforedeactivate
20678              * Fires before a tab is de-activated - can be used to do validation on a form.
20679              * @param {Roo.bootstrap.TabPanel} this
20680              * @return {Boolean} false if there is an error
20681             
20682          */
20683         'beforedeactivate': true
20684      });
20685     
20686     this.tabId = this.tabId || Roo.id();
20687   
20688 };
20689
20690 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20691     
20692     active: false,
20693     html: false,
20694     tabId: false,
20695     navId : false,
20696     href : '',
20697     touchSlide : false,
20698     getAutoCreate : function(){
20699         
20700         
20701         var cfg = {
20702             tag: 'div',
20703             // item is needed for carousel - not sure if it has any effect otherwise
20704             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20705             html: this.html || ''
20706         };
20707         
20708         if(this.active){
20709             cfg.cls += ' active';
20710         }
20711         
20712         if(this.tabId){
20713             cfg.tabId = this.tabId;
20714         }
20715         
20716         
20717         
20718         return cfg;
20719     },
20720     
20721     initEvents:  function()
20722     {
20723         var p = this.parent();
20724         
20725         this.navId = this.navId || p.navId;
20726         
20727         if (typeof(this.navId) != 'undefined') {
20728             // not really needed.. but just in case.. parent should be a NavGroup.
20729             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20730             
20731             tg.register(this);
20732             
20733             var i = tg.tabs.length - 1;
20734             
20735             if(this.active && tg.bullets > 0 && i < tg.bullets){
20736                 tg.setActiveBullet(i);
20737             }
20738         }
20739         
20740         this.el.on('click', this.onClick, this);
20741         
20742         if(Roo.isTouch && this.touchSlide){
20743             this.el.on("touchstart", this.onTouchStart, this);
20744             this.el.on("touchmove", this.onTouchMove, this);
20745             this.el.on("touchend", this.onTouchEnd, this);
20746         }
20747         
20748     },
20749     
20750     onRender : function(ct, position)
20751     {
20752         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20753     },
20754     
20755     setActive : function(state)
20756     {
20757         Roo.log("panel - set active " + this.tabId + "=" + state);
20758         
20759         this.active = state;
20760         if (!state) {
20761             this.el.removeClass('active');
20762             
20763         } else  if (!this.el.hasClass('active')) {
20764             this.el.addClass('active');
20765         }
20766         
20767         this.fireEvent('changed', this, state);
20768     },
20769     
20770     onClick : function(e)
20771     {
20772         e.preventDefault();
20773         
20774         if(!this.href.length){
20775             return;
20776         }
20777         
20778         window.location.href = this.href;
20779     },
20780     
20781     startX : 0,
20782     startY : 0,
20783     endX : 0,
20784     endY : 0,
20785     swiping : false,
20786     
20787     onTouchStart : function(e)
20788     {
20789         this.swiping = false;
20790         
20791         this.startX = e.browserEvent.touches[0].clientX;
20792         this.startY = e.browserEvent.touches[0].clientY;
20793     },
20794     
20795     onTouchMove : function(e)
20796     {
20797         this.swiping = true;
20798         
20799         this.endX = e.browserEvent.touches[0].clientX;
20800         this.endY = e.browserEvent.touches[0].clientY;
20801     },
20802     
20803     onTouchEnd : function(e)
20804     {
20805         if(!this.swiping){
20806             this.onClick(e);
20807             return;
20808         }
20809         
20810         var tabGroup = this.parent();
20811         
20812         if(this.endX > this.startX){ // swiping right
20813             tabGroup.showPanelPrev();
20814             return;
20815         }
20816         
20817         if(this.startX > this.endX){ // swiping left
20818             tabGroup.showPanelNext();
20819             return;
20820         }
20821     }
20822     
20823     
20824 });
20825  
20826
20827  
20828
20829  /*
20830  * - LGPL
20831  *
20832  * DateField
20833  * 
20834  */
20835
20836 /**
20837  * @class Roo.bootstrap.DateField
20838  * @extends Roo.bootstrap.Input
20839  * Bootstrap DateField class
20840  * @cfg {Number} weekStart default 0
20841  * @cfg {String} viewMode default empty, (months|years)
20842  * @cfg {String} minViewMode default empty, (months|years)
20843  * @cfg {Number} startDate default -Infinity
20844  * @cfg {Number} endDate default Infinity
20845  * @cfg {Boolean} todayHighlight default false
20846  * @cfg {Boolean} todayBtn default false
20847  * @cfg {Boolean} calendarWeeks default false
20848  * @cfg {Object} daysOfWeekDisabled default empty
20849  * @cfg {Boolean} singleMode default false (true | false)
20850  * 
20851  * @cfg {Boolean} keyboardNavigation default true
20852  * @cfg {String} language default en
20853  * 
20854  * @constructor
20855  * Create a new DateField
20856  * @param {Object} config The config object
20857  */
20858
20859 Roo.bootstrap.DateField = function(config){
20860     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20861      this.addEvents({
20862             /**
20863              * @event show
20864              * Fires when this field show.
20865              * @param {Roo.bootstrap.DateField} this
20866              * @param {Mixed} date The date value
20867              */
20868             show : true,
20869             /**
20870              * @event show
20871              * Fires when this field hide.
20872              * @param {Roo.bootstrap.DateField} this
20873              * @param {Mixed} date The date value
20874              */
20875             hide : true,
20876             /**
20877              * @event select
20878              * Fires when select a date.
20879              * @param {Roo.bootstrap.DateField} this
20880              * @param {Mixed} date The date value
20881              */
20882             select : true,
20883             /**
20884              * @event beforeselect
20885              * Fires when before select a date.
20886              * @param {Roo.bootstrap.DateField} this
20887              * @param {Mixed} date The date value
20888              */
20889             beforeselect : true
20890         });
20891 };
20892
20893 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20894     
20895     /**
20896      * @cfg {String} format
20897      * The default date format string which can be overriden for localization support.  The format must be
20898      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20899      */
20900     format : "m/d/y",
20901     /**
20902      * @cfg {String} altFormats
20903      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20904      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20905      */
20906     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20907     
20908     weekStart : 0,
20909     
20910     viewMode : '',
20911     
20912     minViewMode : '',
20913     
20914     todayHighlight : false,
20915     
20916     todayBtn: false,
20917     
20918     language: 'en',
20919     
20920     keyboardNavigation: true,
20921     
20922     calendarWeeks: false,
20923     
20924     startDate: -Infinity,
20925     
20926     endDate: Infinity,
20927     
20928     daysOfWeekDisabled: [],
20929     
20930     _events: [],
20931     
20932     singleMode : false,
20933     
20934     UTCDate: function()
20935     {
20936         return new Date(Date.UTC.apply(Date, arguments));
20937     },
20938     
20939     UTCToday: function()
20940     {
20941         var today = new Date();
20942         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20943     },
20944     
20945     getDate: function() {
20946             var d = this.getUTCDate();
20947             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20948     },
20949     
20950     getUTCDate: function() {
20951             return this.date;
20952     },
20953     
20954     setDate: function(d) {
20955             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20956     },
20957     
20958     setUTCDate: function(d) {
20959             this.date = d;
20960             this.setValue(this.formatDate(this.date));
20961     },
20962         
20963     onRender: function(ct, position)
20964     {
20965         
20966         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20967         
20968         this.language = this.language || 'en';
20969         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20970         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20971         
20972         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20973         this.format = this.format || 'm/d/y';
20974         this.isInline = false;
20975         this.isInput = true;
20976         this.component = this.el.select('.add-on', true).first() || false;
20977         this.component = (this.component && this.component.length === 0) ? false : this.component;
20978         this.hasInput = this.component && this.inputEl().length;
20979         
20980         if (typeof(this.minViewMode === 'string')) {
20981             switch (this.minViewMode) {
20982                 case 'months':
20983                     this.minViewMode = 1;
20984                     break;
20985                 case 'years':
20986                     this.minViewMode = 2;
20987                     break;
20988                 default:
20989                     this.minViewMode = 0;
20990                     break;
20991             }
20992         }
20993         
20994         if (typeof(this.viewMode === 'string')) {
20995             switch (this.viewMode) {
20996                 case 'months':
20997                     this.viewMode = 1;
20998                     break;
20999                 case 'years':
21000                     this.viewMode = 2;
21001                     break;
21002                 default:
21003                     this.viewMode = 0;
21004                     break;
21005             }
21006         }
21007                 
21008         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21009         
21010 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21011         
21012         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21013         
21014         this.picker().on('mousedown', this.onMousedown, this);
21015         this.picker().on('click', this.onClick, this);
21016         
21017         this.picker().addClass('datepicker-dropdown');
21018         
21019         this.startViewMode = this.viewMode;
21020         
21021         if(this.singleMode){
21022             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21023                 v.setVisibilityMode(Roo.Element.DISPLAY);
21024                 v.hide();
21025             });
21026             
21027             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21028                 v.setStyle('width', '189px');
21029             });
21030         }
21031         
21032         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21033             if(!this.calendarWeeks){
21034                 v.remove();
21035                 return;
21036             }
21037             
21038             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21039             v.attr('colspan', function(i, val){
21040                 return parseInt(val) + 1;
21041             });
21042         });
21043                         
21044         
21045         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21046         
21047         this.setStartDate(this.startDate);
21048         this.setEndDate(this.endDate);
21049         
21050         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21051         
21052         this.fillDow();
21053         this.fillMonths();
21054         this.update();
21055         this.showMode();
21056         
21057         if(this.isInline) {
21058             this.showPopup();
21059         }
21060     },
21061     
21062     picker : function()
21063     {
21064         return this.pickerEl;
21065 //        return this.el.select('.datepicker', true).first();
21066     },
21067     
21068     fillDow: function()
21069     {
21070         var dowCnt = this.weekStart;
21071         
21072         var dow = {
21073             tag: 'tr',
21074             cn: [
21075                 
21076             ]
21077         };
21078         
21079         if(this.calendarWeeks){
21080             dow.cn.push({
21081                 tag: 'th',
21082                 cls: 'cw',
21083                 html: '&nbsp;'
21084             })
21085         }
21086         
21087         while (dowCnt < this.weekStart + 7) {
21088             dow.cn.push({
21089                 tag: 'th',
21090                 cls: 'dow',
21091                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21092             });
21093         }
21094         
21095         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21096     },
21097     
21098     fillMonths: function()
21099     {    
21100         var i = 0;
21101         var months = this.picker().select('>.datepicker-months td', true).first();
21102         
21103         months.dom.innerHTML = '';
21104         
21105         while (i < 12) {
21106             var month = {
21107                 tag: 'span',
21108                 cls: 'month',
21109                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21110             };
21111             
21112             months.createChild(month);
21113         }
21114         
21115     },
21116     
21117     update: function()
21118     {
21119         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;
21120         
21121         if (this.date < this.startDate) {
21122             this.viewDate = new Date(this.startDate);
21123         } else if (this.date > this.endDate) {
21124             this.viewDate = new Date(this.endDate);
21125         } else {
21126             this.viewDate = new Date(this.date);
21127         }
21128         
21129         this.fill();
21130     },
21131     
21132     fill: function() 
21133     {
21134         var d = new Date(this.viewDate),
21135                 year = d.getUTCFullYear(),
21136                 month = d.getUTCMonth(),
21137                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21138                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21139                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21140                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21141                 currentDate = this.date && this.date.valueOf(),
21142                 today = this.UTCToday();
21143         
21144         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21145         
21146 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21147         
21148 //        this.picker.select('>tfoot th.today').
21149 //                                              .text(dates[this.language].today)
21150 //                                              .toggle(this.todayBtn !== false);
21151     
21152         this.updateNavArrows();
21153         this.fillMonths();
21154                                                 
21155         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21156         
21157         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21158          
21159         prevMonth.setUTCDate(day);
21160         
21161         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21162         
21163         var nextMonth = new Date(prevMonth);
21164         
21165         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21166         
21167         nextMonth = nextMonth.valueOf();
21168         
21169         var fillMonths = false;
21170         
21171         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21172         
21173         while(prevMonth.valueOf() <= nextMonth) {
21174             var clsName = '';
21175             
21176             if (prevMonth.getUTCDay() === this.weekStart) {
21177                 if(fillMonths){
21178                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21179                 }
21180                     
21181                 fillMonths = {
21182                     tag: 'tr',
21183                     cn: []
21184                 };
21185                 
21186                 if(this.calendarWeeks){
21187                     // ISO 8601: First week contains first thursday.
21188                     // ISO also states week starts on Monday, but we can be more abstract here.
21189                     var
21190                     // Start of current week: based on weekstart/current date
21191                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21192                     // Thursday of this week
21193                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21194                     // First Thursday of year, year from thursday
21195                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21196                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21197                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21198                     
21199                     fillMonths.cn.push({
21200                         tag: 'td',
21201                         cls: 'cw',
21202                         html: calWeek
21203                     });
21204                 }
21205             }
21206             
21207             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21208                 clsName += ' old';
21209             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21210                 clsName += ' new';
21211             }
21212             if (this.todayHighlight &&
21213                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21214                 prevMonth.getUTCMonth() == today.getMonth() &&
21215                 prevMonth.getUTCDate() == today.getDate()) {
21216                 clsName += ' today';
21217             }
21218             
21219             if (currentDate && prevMonth.valueOf() === currentDate) {
21220                 clsName += ' active';
21221             }
21222             
21223             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21224                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21225                     clsName += ' disabled';
21226             }
21227             
21228             fillMonths.cn.push({
21229                 tag: 'td',
21230                 cls: 'day ' + clsName,
21231                 html: prevMonth.getDate()
21232             });
21233             
21234             prevMonth.setDate(prevMonth.getDate()+1);
21235         }
21236           
21237         var currentYear = this.date && this.date.getUTCFullYear();
21238         var currentMonth = this.date && this.date.getUTCMonth();
21239         
21240         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21241         
21242         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21243             v.removeClass('active');
21244             
21245             if(currentYear === year && k === currentMonth){
21246                 v.addClass('active');
21247             }
21248             
21249             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21250                 v.addClass('disabled');
21251             }
21252             
21253         });
21254         
21255         
21256         year = parseInt(year/10, 10) * 10;
21257         
21258         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21259         
21260         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21261         
21262         year -= 1;
21263         for (var i = -1; i < 11; i++) {
21264             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21265                 tag: 'span',
21266                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21267                 html: year
21268             });
21269             
21270             year += 1;
21271         }
21272     },
21273     
21274     showMode: function(dir) 
21275     {
21276         if (dir) {
21277             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21278         }
21279         
21280         Roo.each(this.picker().select('>div',true).elements, function(v){
21281             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21282             v.hide();
21283         });
21284         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21285     },
21286     
21287     place: function()
21288     {
21289         if(this.isInline) {
21290             return;
21291         }
21292         
21293         this.picker().removeClass(['bottom', 'top']);
21294         
21295         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21296             /*
21297              * place to the top of element!
21298              *
21299              */
21300             
21301             this.picker().addClass('top');
21302             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21303             
21304             return;
21305         }
21306         
21307         this.picker().addClass('bottom');
21308         
21309         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21310     },
21311     
21312     parseDate : function(value)
21313     {
21314         if(!value || value instanceof Date){
21315             return value;
21316         }
21317         var v = Date.parseDate(value, this.format);
21318         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21319             v = Date.parseDate(value, 'Y-m-d');
21320         }
21321         if(!v && this.altFormats){
21322             if(!this.altFormatsArray){
21323                 this.altFormatsArray = this.altFormats.split("|");
21324             }
21325             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21326                 v = Date.parseDate(value, this.altFormatsArray[i]);
21327             }
21328         }
21329         return v;
21330     },
21331     
21332     formatDate : function(date, fmt)
21333     {   
21334         return (!date || !(date instanceof Date)) ?
21335         date : date.dateFormat(fmt || this.format);
21336     },
21337     
21338     onFocus : function()
21339     {
21340         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21341         this.showPopup();
21342     },
21343     
21344     onBlur : function()
21345     {
21346         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21347         
21348         var d = this.inputEl().getValue();
21349         
21350         this.setValue(d);
21351                 
21352         this.hidePopup();
21353     },
21354     
21355     showPopup : function()
21356     {
21357         this.picker().show();
21358         this.update();
21359         this.place();
21360         
21361         this.fireEvent('showpopup', this, this.date);
21362     },
21363     
21364     hidePopup : function()
21365     {
21366         if(this.isInline) {
21367             return;
21368         }
21369         this.picker().hide();
21370         this.viewMode = this.startViewMode;
21371         this.showMode();
21372         
21373         this.fireEvent('hidepopup', this, this.date);
21374         
21375     },
21376     
21377     onMousedown: function(e)
21378     {
21379         e.stopPropagation();
21380         e.preventDefault();
21381     },
21382     
21383     keyup: function(e)
21384     {
21385         Roo.bootstrap.DateField.superclass.keyup.call(this);
21386         this.update();
21387     },
21388
21389     setValue: function(v)
21390     {
21391         if(this.fireEvent('beforeselect', this, v) !== false){
21392             var d = new Date(this.parseDate(v) ).clearTime();
21393         
21394             if(isNaN(d.getTime())){
21395                 this.date = this.viewDate = '';
21396                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21397                 return;
21398             }
21399
21400             v = this.formatDate(d);
21401
21402             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21403
21404             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21405
21406             this.update();
21407
21408             this.fireEvent('select', this, this.date);
21409         }
21410     },
21411     
21412     getValue: function()
21413     {
21414         return this.formatDate(this.date);
21415     },
21416     
21417     fireKey: function(e)
21418     {
21419         if (!this.picker().isVisible()){
21420             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21421                 this.showPopup();
21422             }
21423             return;
21424         }
21425         
21426         var dateChanged = false,
21427         dir, day, month,
21428         newDate, newViewDate;
21429         
21430         switch(e.keyCode){
21431             case 27: // escape
21432                 this.hidePopup();
21433                 e.preventDefault();
21434                 break;
21435             case 37: // left
21436             case 39: // right
21437                 if (!this.keyboardNavigation) {
21438                     break;
21439                 }
21440                 dir = e.keyCode == 37 ? -1 : 1;
21441                 
21442                 if (e.ctrlKey){
21443                     newDate = this.moveYear(this.date, dir);
21444                     newViewDate = this.moveYear(this.viewDate, dir);
21445                 } else if (e.shiftKey){
21446                     newDate = this.moveMonth(this.date, dir);
21447                     newViewDate = this.moveMonth(this.viewDate, dir);
21448                 } else {
21449                     newDate = new Date(this.date);
21450                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21451                     newViewDate = new Date(this.viewDate);
21452                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21453                 }
21454                 if (this.dateWithinRange(newDate)){
21455                     this.date = newDate;
21456                     this.viewDate = newViewDate;
21457                     this.setValue(this.formatDate(this.date));
21458 //                    this.update();
21459                     e.preventDefault();
21460                     dateChanged = true;
21461                 }
21462                 break;
21463             case 38: // up
21464             case 40: // down
21465                 if (!this.keyboardNavigation) {
21466                     break;
21467                 }
21468                 dir = e.keyCode == 38 ? -1 : 1;
21469                 if (e.ctrlKey){
21470                     newDate = this.moveYear(this.date, dir);
21471                     newViewDate = this.moveYear(this.viewDate, dir);
21472                 } else if (e.shiftKey){
21473                     newDate = this.moveMonth(this.date, dir);
21474                     newViewDate = this.moveMonth(this.viewDate, dir);
21475                 } else {
21476                     newDate = new Date(this.date);
21477                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21478                     newViewDate = new Date(this.viewDate);
21479                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21480                 }
21481                 if (this.dateWithinRange(newDate)){
21482                     this.date = newDate;
21483                     this.viewDate = newViewDate;
21484                     this.setValue(this.formatDate(this.date));
21485 //                    this.update();
21486                     e.preventDefault();
21487                     dateChanged = true;
21488                 }
21489                 break;
21490             case 13: // enter
21491                 this.setValue(this.formatDate(this.date));
21492                 this.hidePopup();
21493                 e.preventDefault();
21494                 break;
21495             case 9: // tab
21496                 this.setValue(this.formatDate(this.date));
21497                 this.hidePopup();
21498                 break;
21499             case 16: // shift
21500             case 17: // ctrl
21501             case 18: // alt
21502                 break;
21503             default :
21504                 this.hidePopup();
21505                 
21506         }
21507     },
21508     
21509     
21510     onClick: function(e) 
21511     {
21512         e.stopPropagation();
21513         e.preventDefault();
21514         
21515         var target = e.getTarget();
21516         
21517         if(target.nodeName.toLowerCase() === 'i'){
21518             target = Roo.get(target).dom.parentNode;
21519         }
21520         
21521         var nodeName = target.nodeName;
21522         var className = target.className;
21523         var html = target.innerHTML;
21524         //Roo.log(nodeName);
21525         
21526         switch(nodeName.toLowerCase()) {
21527             case 'th':
21528                 switch(className) {
21529                     case 'switch':
21530                         this.showMode(1);
21531                         break;
21532                     case 'prev':
21533                     case 'next':
21534                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21535                         switch(this.viewMode){
21536                                 case 0:
21537                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21538                                         break;
21539                                 case 1:
21540                                 case 2:
21541                                         this.viewDate = this.moveYear(this.viewDate, dir);
21542                                         break;
21543                         }
21544                         this.fill();
21545                         break;
21546                     case 'today':
21547                         var date = new Date();
21548                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21549 //                        this.fill()
21550                         this.setValue(this.formatDate(this.date));
21551                         
21552                         this.hidePopup();
21553                         break;
21554                 }
21555                 break;
21556             case 'span':
21557                 if (className.indexOf('disabled') < 0) {
21558                     this.viewDate.setUTCDate(1);
21559                     if (className.indexOf('month') > -1) {
21560                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21561                     } else {
21562                         var year = parseInt(html, 10) || 0;
21563                         this.viewDate.setUTCFullYear(year);
21564                         
21565                     }
21566                     
21567                     if(this.singleMode){
21568                         this.setValue(this.formatDate(this.viewDate));
21569                         this.hidePopup();
21570                         return;
21571                     }
21572                     
21573                     this.showMode(-1);
21574                     this.fill();
21575                 }
21576                 break;
21577                 
21578             case 'td':
21579                 //Roo.log(className);
21580                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21581                     var day = parseInt(html, 10) || 1;
21582                     var year = this.viewDate.getUTCFullYear(),
21583                         month = this.viewDate.getUTCMonth();
21584
21585                     if (className.indexOf('old') > -1) {
21586                         if(month === 0 ){
21587                             month = 11;
21588                             year -= 1;
21589                         }else{
21590                             month -= 1;
21591                         }
21592                     } else if (className.indexOf('new') > -1) {
21593                         if (month == 11) {
21594                             month = 0;
21595                             year += 1;
21596                         } else {
21597                             month += 1;
21598                         }
21599                     }
21600                     //Roo.log([year,month,day]);
21601                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21602                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21603 //                    this.fill();
21604                     //Roo.log(this.formatDate(this.date));
21605                     this.setValue(this.formatDate(this.date));
21606                     this.hidePopup();
21607                 }
21608                 break;
21609         }
21610     },
21611     
21612     setStartDate: function(startDate)
21613     {
21614         this.startDate = startDate || -Infinity;
21615         if (this.startDate !== -Infinity) {
21616             this.startDate = this.parseDate(this.startDate);
21617         }
21618         this.update();
21619         this.updateNavArrows();
21620     },
21621
21622     setEndDate: function(endDate)
21623     {
21624         this.endDate = endDate || Infinity;
21625         if (this.endDate !== Infinity) {
21626             this.endDate = this.parseDate(this.endDate);
21627         }
21628         this.update();
21629         this.updateNavArrows();
21630     },
21631     
21632     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21633     {
21634         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21635         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21636             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21637         }
21638         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21639             return parseInt(d, 10);
21640         });
21641         this.update();
21642         this.updateNavArrows();
21643     },
21644     
21645     updateNavArrows: function() 
21646     {
21647         if(this.singleMode){
21648             return;
21649         }
21650         
21651         var d = new Date(this.viewDate),
21652         year = d.getUTCFullYear(),
21653         month = d.getUTCMonth();
21654         
21655         Roo.each(this.picker().select('.prev', true).elements, function(v){
21656             v.show();
21657             switch (this.viewMode) {
21658                 case 0:
21659
21660                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21661                         v.hide();
21662                     }
21663                     break;
21664                 case 1:
21665                 case 2:
21666                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21667                         v.hide();
21668                     }
21669                     break;
21670             }
21671         });
21672         
21673         Roo.each(this.picker().select('.next', true).elements, function(v){
21674             v.show();
21675             switch (this.viewMode) {
21676                 case 0:
21677
21678                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21679                         v.hide();
21680                     }
21681                     break;
21682                 case 1:
21683                 case 2:
21684                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21685                         v.hide();
21686                     }
21687                     break;
21688             }
21689         })
21690     },
21691     
21692     moveMonth: function(date, dir)
21693     {
21694         if (!dir) {
21695             return date;
21696         }
21697         var new_date = new Date(date.valueOf()),
21698         day = new_date.getUTCDate(),
21699         month = new_date.getUTCMonth(),
21700         mag = Math.abs(dir),
21701         new_month, test;
21702         dir = dir > 0 ? 1 : -1;
21703         if (mag == 1){
21704             test = dir == -1
21705             // If going back one month, make sure month is not current month
21706             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21707             ? function(){
21708                 return new_date.getUTCMonth() == month;
21709             }
21710             // If going forward one month, make sure month is as expected
21711             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21712             : function(){
21713                 return new_date.getUTCMonth() != new_month;
21714             };
21715             new_month = month + dir;
21716             new_date.setUTCMonth(new_month);
21717             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21718             if (new_month < 0 || new_month > 11) {
21719                 new_month = (new_month + 12) % 12;
21720             }
21721         } else {
21722             // For magnitudes >1, move one month at a time...
21723             for (var i=0; i<mag; i++) {
21724                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21725                 new_date = this.moveMonth(new_date, dir);
21726             }
21727             // ...then reset the day, keeping it in the new month
21728             new_month = new_date.getUTCMonth();
21729             new_date.setUTCDate(day);
21730             test = function(){
21731                 return new_month != new_date.getUTCMonth();
21732             };
21733         }
21734         // Common date-resetting loop -- if date is beyond end of month, make it
21735         // end of month
21736         while (test()){
21737             new_date.setUTCDate(--day);
21738             new_date.setUTCMonth(new_month);
21739         }
21740         return new_date;
21741     },
21742
21743     moveYear: function(date, dir)
21744     {
21745         return this.moveMonth(date, dir*12);
21746     },
21747
21748     dateWithinRange: function(date)
21749     {
21750         return date >= this.startDate && date <= this.endDate;
21751     },
21752
21753     
21754     remove: function() 
21755     {
21756         this.picker().remove();
21757     },
21758     
21759     validateValue : function(value)
21760     {
21761         if(this.getVisibilityEl().hasClass('hidden')){
21762             return true;
21763         }
21764         
21765         if(value.length < 1)  {
21766             if(this.allowBlank){
21767                 return true;
21768             }
21769             return false;
21770         }
21771         
21772         if(value.length < this.minLength){
21773             return false;
21774         }
21775         if(value.length > this.maxLength){
21776             return false;
21777         }
21778         if(this.vtype){
21779             var vt = Roo.form.VTypes;
21780             if(!vt[this.vtype](value, this)){
21781                 return false;
21782             }
21783         }
21784         if(typeof this.validator == "function"){
21785             var msg = this.validator(value);
21786             if(msg !== true){
21787                 return false;
21788             }
21789         }
21790         
21791         if(this.regex && !this.regex.test(value)){
21792             return false;
21793         }
21794         
21795         if(typeof(this.parseDate(value)) == 'undefined'){
21796             return false;
21797         }
21798         
21799         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21800             return false;
21801         }      
21802         
21803         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21804             return false;
21805         } 
21806         
21807         
21808         return true;
21809     },
21810     
21811     reset : function()
21812     {
21813         this.date = this.viewDate = '';
21814         
21815         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21816     }
21817    
21818 });
21819
21820 Roo.apply(Roo.bootstrap.DateField,  {
21821     
21822     head : {
21823         tag: 'thead',
21824         cn: [
21825         {
21826             tag: 'tr',
21827             cn: [
21828             {
21829                 tag: 'th',
21830                 cls: 'prev',
21831                 html: '<i class="fa fa-arrow-left"/>'
21832             },
21833             {
21834                 tag: 'th',
21835                 cls: 'switch',
21836                 colspan: '5'
21837             },
21838             {
21839                 tag: 'th',
21840                 cls: 'next',
21841                 html: '<i class="fa fa-arrow-right"/>'
21842             }
21843
21844             ]
21845         }
21846         ]
21847     },
21848     
21849     content : {
21850         tag: 'tbody',
21851         cn: [
21852         {
21853             tag: 'tr',
21854             cn: [
21855             {
21856                 tag: 'td',
21857                 colspan: '7'
21858             }
21859             ]
21860         }
21861         ]
21862     },
21863     
21864     footer : {
21865         tag: 'tfoot',
21866         cn: [
21867         {
21868             tag: 'tr',
21869             cn: [
21870             {
21871                 tag: 'th',
21872                 colspan: '7',
21873                 cls: 'today'
21874             }
21875                     
21876             ]
21877         }
21878         ]
21879     },
21880     
21881     dates:{
21882         en: {
21883             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21884             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21885             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21886             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21887             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21888             today: "Today"
21889         }
21890     },
21891     
21892     modes: [
21893     {
21894         clsName: 'days',
21895         navFnc: 'Month',
21896         navStep: 1
21897     },
21898     {
21899         clsName: 'months',
21900         navFnc: 'FullYear',
21901         navStep: 1
21902     },
21903     {
21904         clsName: 'years',
21905         navFnc: 'FullYear',
21906         navStep: 10
21907     }]
21908 });
21909
21910 Roo.apply(Roo.bootstrap.DateField,  {
21911   
21912     template : {
21913         tag: 'div',
21914         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21915         cn: [
21916         {
21917             tag: 'div',
21918             cls: 'datepicker-days',
21919             cn: [
21920             {
21921                 tag: 'table',
21922                 cls: 'table-condensed',
21923                 cn:[
21924                 Roo.bootstrap.DateField.head,
21925                 {
21926                     tag: 'tbody'
21927                 },
21928                 Roo.bootstrap.DateField.footer
21929                 ]
21930             }
21931             ]
21932         },
21933         {
21934             tag: 'div',
21935             cls: 'datepicker-months',
21936             cn: [
21937             {
21938                 tag: 'table',
21939                 cls: 'table-condensed',
21940                 cn:[
21941                 Roo.bootstrap.DateField.head,
21942                 Roo.bootstrap.DateField.content,
21943                 Roo.bootstrap.DateField.footer
21944                 ]
21945             }
21946             ]
21947         },
21948         {
21949             tag: 'div',
21950             cls: 'datepicker-years',
21951             cn: [
21952             {
21953                 tag: 'table',
21954                 cls: 'table-condensed',
21955                 cn:[
21956                 Roo.bootstrap.DateField.head,
21957                 Roo.bootstrap.DateField.content,
21958                 Roo.bootstrap.DateField.footer
21959                 ]
21960             }
21961             ]
21962         }
21963         ]
21964     }
21965 });
21966
21967  
21968
21969  /*
21970  * - LGPL
21971  *
21972  * TimeField
21973  * 
21974  */
21975
21976 /**
21977  * @class Roo.bootstrap.TimeField
21978  * @extends Roo.bootstrap.Input
21979  * Bootstrap DateField class
21980  * 
21981  * 
21982  * @constructor
21983  * Create a new TimeField
21984  * @param {Object} config The config object
21985  */
21986
21987 Roo.bootstrap.TimeField = function(config){
21988     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21989     this.addEvents({
21990             /**
21991              * @event show
21992              * Fires when this field show.
21993              * @param {Roo.bootstrap.DateField} thisthis
21994              * @param {Mixed} date The date value
21995              */
21996             show : true,
21997             /**
21998              * @event show
21999              * Fires when this field hide.
22000              * @param {Roo.bootstrap.DateField} this
22001              * @param {Mixed} date The date value
22002              */
22003             hide : true,
22004             /**
22005              * @event select
22006              * Fires when select a date.
22007              * @param {Roo.bootstrap.DateField} this
22008              * @param {Mixed} date The date value
22009              */
22010             select : true
22011         });
22012 };
22013
22014 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22015     
22016     /**
22017      * @cfg {String} format
22018      * The default time format string which can be overriden for localization support.  The format must be
22019      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22020      */
22021     format : "H:i",
22022
22023     getAutoCreate : function()
22024     {
22025         this.after = '<i class="fa far fa-clock"></i>';
22026         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22027         
22028          
22029     },
22030     onRender: function(ct, position)
22031     {
22032         
22033         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22034                 
22035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22036         
22037         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22038         
22039         this.pop = this.picker().select('>.datepicker-time',true).first();
22040         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22041         
22042         this.picker().on('mousedown', this.onMousedown, this);
22043         this.picker().on('click', this.onClick, this);
22044         
22045         this.picker().addClass('datepicker-dropdown');
22046     
22047         this.fillTime();
22048         this.update();
22049             
22050         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22051         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22052         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22053         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22054         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22055         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22056
22057     },
22058     
22059     fireKey: function(e){
22060         if (!this.picker().isVisible()){
22061             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22062                 this.show();
22063             }
22064             return;
22065         }
22066
22067         e.preventDefault();
22068         
22069         switch(e.keyCode){
22070             case 27: // escape
22071                 this.hide();
22072                 break;
22073             case 37: // left
22074             case 39: // right
22075                 this.onTogglePeriod();
22076                 break;
22077             case 38: // up
22078                 this.onIncrementMinutes();
22079                 break;
22080             case 40: // down
22081                 this.onDecrementMinutes();
22082                 break;
22083             case 13: // enter
22084             case 9: // tab
22085                 this.setTime();
22086                 break;
22087         }
22088     },
22089     
22090     onClick: function(e) {
22091         e.stopPropagation();
22092         e.preventDefault();
22093     },
22094     
22095     picker : function()
22096     {
22097         return this.pickerEl;
22098     },
22099     
22100     fillTime: function()
22101     {    
22102         var time = this.pop.select('tbody', true).first();
22103         
22104         time.dom.innerHTML = '';
22105         
22106         time.createChild({
22107             tag: 'tr',
22108             cn: [
22109                 {
22110                     tag: 'td',
22111                     cn: [
22112                         {
22113                             tag: 'a',
22114                             href: '#',
22115                             cls: 'btn',
22116                             cn: [
22117                                 {
22118                                     tag: 'i',
22119                                     cls: 'hours-up fa fas fa-chevron-up'
22120                                 }
22121                             ]
22122                         } 
22123                     ]
22124                 },
22125                 {
22126                     tag: 'td',
22127                     cls: 'separator'
22128                 },
22129                 {
22130                     tag: 'td',
22131                     cn: [
22132                         {
22133                             tag: 'a',
22134                             href: '#',
22135                             cls: 'btn',
22136                             cn: [
22137                                 {
22138                                     tag: 'i',
22139                                     cls: 'minutes-up fa fas fa-chevron-up'
22140                                 }
22141                             ]
22142                         }
22143                     ]
22144                 },
22145                 {
22146                     tag: 'td',
22147                     cls: 'separator'
22148                 }
22149             ]
22150         });
22151         
22152         time.createChild({
22153             tag: 'tr',
22154             cn: [
22155                 {
22156                     tag: 'td',
22157                     cn: [
22158                         {
22159                             tag: 'span',
22160                             cls: 'timepicker-hour',
22161                             html: '00'
22162                         }  
22163                     ]
22164                 },
22165                 {
22166                     tag: 'td',
22167                     cls: 'separator',
22168                     html: ':'
22169                 },
22170                 {
22171                     tag: 'td',
22172                     cn: [
22173                         {
22174                             tag: 'span',
22175                             cls: 'timepicker-minute',
22176                             html: '00'
22177                         }  
22178                     ]
22179                 },
22180                 {
22181                     tag: 'td',
22182                     cls: 'separator'
22183                 },
22184                 {
22185                     tag: 'td',
22186                     cn: [
22187                         {
22188                             tag: 'button',
22189                             type: 'button',
22190                             cls: 'btn btn-primary period',
22191                             html: 'AM'
22192                             
22193                         }
22194                     ]
22195                 }
22196             ]
22197         });
22198         
22199         time.createChild({
22200             tag: 'tr',
22201             cn: [
22202                 {
22203                     tag: 'td',
22204                     cn: [
22205                         {
22206                             tag: 'a',
22207                             href: '#',
22208                             cls: 'btn',
22209                             cn: [
22210                                 {
22211                                     tag: 'span',
22212                                     cls: 'hours-down fa fas fa-chevron-down'
22213                                 }
22214                             ]
22215                         }
22216                     ]
22217                 },
22218                 {
22219                     tag: 'td',
22220                     cls: 'separator'
22221                 },
22222                 {
22223                     tag: 'td',
22224                     cn: [
22225                         {
22226                             tag: 'a',
22227                             href: '#',
22228                             cls: 'btn',
22229                             cn: [
22230                                 {
22231                                     tag: 'span',
22232                                     cls: 'minutes-down fa fas fa-chevron-down'
22233                                 }
22234                             ]
22235                         }
22236                     ]
22237                 },
22238                 {
22239                     tag: 'td',
22240                     cls: 'separator'
22241                 }
22242             ]
22243         });
22244         
22245     },
22246     
22247     update: function()
22248     {
22249         
22250         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22251         
22252         this.fill();
22253     },
22254     
22255     fill: function() 
22256     {
22257         var hours = this.time.getHours();
22258         var minutes = this.time.getMinutes();
22259         var period = 'AM';
22260         
22261         if(hours > 11){
22262             period = 'PM';
22263         }
22264         
22265         if(hours == 0){
22266             hours = 12;
22267         }
22268         
22269         
22270         if(hours > 12){
22271             hours = hours - 12;
22272         }
22273         
22274         if(hours < 10){
22275             hours = '0' + hours;
22276         }
22277         
22278         if(minutes < 10){
22279             minutes = '0' + minutes;
22280         }
22281         
22282         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22283         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22284         this.pop.select('button', true).first().dom.innerHTML = period;
22285         
22286     },
22287     
22288     place: function()
22289     {   
22290         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22291         
22292         var cls = ['bottom'];
22293         
22294         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22295             cls.pop();
22296             cls.push('top');
22297         }
22298         
22299         cls.push('right');
22300         
22301         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22302             cls.pop();
22303             cls.push('left');
22304         }
22305         //this.picker().setXY(20000,20000);
22306         this.picker().addClass(cls.join('-'));
22307         
22308         var _this = this;
22309         
22310         Roo.each(cls, function(c){
22311             if(c == 'bottom'){
22312                 (function() {
22313                  //  
22314                 }).defer(200);
22315                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22316                 //_this.picker().setTop(_this.inputEl().getHeight());
22317                 return;
22318             }
22319             if(c == 'top'){
22320                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22321                 
22322                 //_this.picker().setTop(0 - _this.picker().getHeight());
22323                 return;
22324             }
22325             /*
22326             if(c == 'left'){
22327                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22328                 return;
22329             }
22330             if(c == 'right'){
22331                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22332                 return;
22333             }
22334             */
22335         });
22336         
22337     },
22338   
22339     onFocus : function()
22340     {
22341         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22342         this.show();
22343     },
22344     
22345     onBlur : function()
22346     {
22347         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22348         this.hide();
22349     },
22350     
22351     show : function()
22352     {
22353         this.picker().show();
22354         this.pop.show();
22355         this.update();
22356         this.place();
22357         
22358         this.fireEvent('show', this, this.date);
22359     },
22360     
22361     hide : function()
22362     {
22363         this.picker().hide();
22364         this.pop.hide();
22365         
22366         this.fireEvent('hide', this, this.date);
22367     },
22368     
22369     setTime : function()
22370     {
22371         this.hide();
22372         this.setValue(this.time.format(this.format));
22373         
22374         this.fireEvent('select', this, this.date);
22375         
22376         
22377     },
22378     
22379     onMousedown: function(e){
22380         e.stopPropagation();
22381         e.preventDefault();
22382     },
22383     
22384     onIncrementHours: function()
22385     {
22386         Roo.log('onIncrementHours');
22387         this.time = this.time.add(Date.HOUR, 1);
22388         this.update();
22389         
22390     },
22391     
22392     onDecrementHours: function()
22393     {
22394         Roo.log('onDecrementHours');
22395         this.time = this.time.add(Date.HOUR, -1);
22396         this.update();
22397     },
22398     
22399     onIncrementMinutes: function()
22400     {
22401         Roo.log('onIncrementMinutes');
22402         this.time = this.time.add(Date.MINUTE, 1);
22403         this.update();
22404     },
22405     
22406     onDecrementMinutes: function()
22407     {
22408         Roo.log('onDecrementMinutes');
22409         this.time = this.time.add(Date.MINUTE, -1);
22410         this.update();
22411     },
22412     
22413     onTogglePeriod: function()
22414     {
22415         Roo.log('onTogglePeriod');
22416         this.time = this.time.add(Date.HOUR, 12);
22417         this.update();
22418     }
22419     
22420    
22421 });
22422  
22423
22424 Roo.apply(Roo.bootstrap.TimeField,  {
22425   
22426     template : {
22427         tag: 'div',
22428         cls: 'datepicker dropdown-menu',
22429         cn: [
22430             {
22431                 tag: 'div',
22432                 cls: 'datepicker-time',
22433                 cn: [
22434                 {
22435                     tag: 'table',
22436                     cls: 'table-condensed',
22437                     cn:[
22438                         {
22439                             tag: 'tbody',
22440                             cn: [
22441                                 {
22442                                     tag: 'tr',
22443                                     cn: [
22444                                     {
22445                                         tag: 'td',
22446                                         colspan: '7'
22447                                     }
22448                                     ]
22449                                 }
22450                             ]
22451                         },
22452                         {
22453                             tag: 'tfoot',
22454                             cn: [
22455                                 {
22456                                     tag: 'tr',
22457                                     cn: [
22458                                     {
22459                                         tag: 'th',
22460                                         colspan: '7',
22461                                         cls: '',
22462                                         cn: [
22463                                             {
22464                                                 tag: 'button',
22465                                                 cls: 'btn btn-info ok',
22466                                                 html: 'OK'
22467                                             }
22468                                         ]
22469                                     }
22470                     
22471                                     ]
22472                                 }
22473                             ]
22474                         }
22475                     ]
22476                 }
22477                 ]
22478             }
22479         ]
22480     }
22481 });
22482
22483  
22484
22485  /*
22486  * - LGPL
22487  *
22488  * MonthField
22489  * 
22490  */
22491
22492 /**
22493  * @class Roo.bootstrap.MonthField
22494  * @extends Roo.bootstrap.Input
22495  * Bootstrap MonthField class
22496  * 
22497  * @cfg {String} language default en
22498  * 
22499  * @constructor
22500  * Create a new MonthField
22501  * @param {Object} config The config object
22502  */
22503
22504 Roo.bootstrap.MonthField = function(config){
22505     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22506     
22507     this.addEvents({
22508         /**
22509          * @event show
22510          * Fires when this field show.
22511          * @param {Roo.bootstrap.MonthField} this
22512          * @param {Mixed} date The date value
22513          */
22514         show : true,
22515         /**
22516          * @event show
22517          * Fires when this field hide.
22518          * @param {Roo.bootstrap.MonthField} this
22519          * @param {Mixed} date The date value
22520          */
22521         hide : true,
22522         /**
22523          * @event select
22524          * Fires when select a date.
22525          * @param {Roo.bootstrap.MonthField} this
22526          * @param {String} oldvalue The old value
22527          * @param {String} newvalue The new value
22528          */
22529         select : true
22530     });
22531 };
22532
22533 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22534     
22535     onRender: function(ct, position)
22536     {
22537         
22538         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22539         
22540         this.language = this.language || 'en';
22541         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22542         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22543         
22544         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22545         this.isInline = false;
22546         this.isInput = true;
22547         this.component = this.el.select('.add-on', true).first() || false;
22548         this.component = (this.component && this.component.length === 0) ? false : this.component;
22549         this.hasInput = this.component && this.inputEL().length;
22550         
22551         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22552         
22553         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22554         
22555         this.picker().on('mousedown', this.onMousedown, this);
22556         this.picker().on('click', this.onClick, this);
22557         
22558         this.picker().addClass('datepicker-dropdown');
22559         
22560         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22561             v.setStyle('width', '189px');
22562         });
22563         
22564         this.fillMonths();
22565         
22566         this.update();
22567         
22568         if(this.isInline) {
22569             this.show();
22570         }
22571         
22572     },
22573     
22574     setValue: function(v, suppressEvent)
22575     {   
22576         var o = this.getValue();
22577         
22578         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22579         
22580         this.update();
22581
22582         if(suppressEvent !== true){
22583             this.fireEvent('select', this, o, v);
22584         }
22585         
22586     },
22587     
22588     getValue: function()
22589     {
22590         return this.value;
22591     },
22592     
22593     onClick: function(e) 
22594     {
22595         e.stopPropagation();
22596         e.preventDefault();
22597         
22598         var target = e.getTarget();
22599         
22600         if(target.nodeName.toLowerCase() === 'i'){
22601             target = Roo.get(target).dom.parentNode;
22602         }
22603         
22604         var nodeName = target.nodeName;
22605         var className = target.className;
22606         var html = target.innerHTML;
22607         
22608         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22609             return;
22610         }
22611         
22612         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22613         
22614         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22615         
22616         this.hide();
22617                         
22618     },
22619     
22620     picker : function()
22621     {
22622         return this.pickerEl;
22623     },
22624     
22625     fillMonths: function()
22626     {    
22627         var i = 0;
22628         var months = this.picker().select('>.datepicker-months td', true).first();
22629         
22630         months.dom.innerHTML = '';
22631         
22632         while (i < 12) {
22633             var month = {
22634                 tag: 'span',
22635                 cls: 'month',
22636                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22637             };
22638             
22639             months.createChild(month);
22640         }
22641         
22642     },
22643     
22644     update: function()
22645     {
22646         var _this = this;
22647         
22648         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22649             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22650         }
22651         
22652         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22653             e.removeClass('active');
22654             
22655             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22656                 e.addClass('active');
22657             }
22658         })
22659     },
22660     
22661     place: function()
22662     {
22663         if(this.isInline) {
22664             return;
22665         }
22666         
22667         this.picker().removeClass(['bottom', 'top']);
22668         
22669         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22670             /*
22671              * place to the top of element!
22672              *
22673              */
22674             
22675             this.picker().addClass('top');
22676             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22677             
22678             return;
22679         }
22680         
22681         this.picker().addClass('bottom');
22682         
22683         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22684     },
22685     
22686     onFocus : function()
22687     {
22688         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22689         this.show();
22690     },
22691     
22692     onBlur : function()
22693     {
22694         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22695         
22696         var d = this.inputEl().getValue();
22697         
22698         this.setValue(d);
22699                 
22700         this.hide();
22701     },
22702     
22703     show : function()
22704     {
22705         this.picker().show();
22706         this.picker().select('>.datepicker-months', true).first().show();
22707         this.update();
22708         this.place();
22709         
22710         this.fireEvent('show', this, this.date);
22711     },
22712     
22713     hide : function()
22714     {
22715         if(this.isInline) {
22716             return;
22717         }
22718         this.picker().hide();
22719         this.fireEvent('hide', this, this.date);
22720         
22721     },
22722     
22723     onMousedown: function(e)
22724     {
22725         e.stopPropagation();
22726         e.preventDefault();
22727     },
22728     
22729     keyup: function(e)
22730     {
22731         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22732         this.update();
22733     },
22734
22735     fireKey: function(e)
22736     {
22737         if (!this.picker().isVisible()){
22738             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22739                 this.show();
22740             }
22741             return;
22742         }
22743         
22744         var dir;
22745         
22746         switch(e.keyCode){
22747             case 27: // escape
22748                 this.hide();
22749                 e.preventDefault();
22750                 break;
22751             case 37: // left
22752             case 39: // right
22753                 dir = e.keyCode == 37 ? -1 : 1;
22754                 
22755                 this.vIndex = this.vIndex + dir;
22756                 
22757                 if(this.vIndex < 0){
22758                     this.vIndex = 0;
22759                 }
22760                 
22761                 if(this.vIndex > 11){
22762                     this.vIndex = 11;
22763                 }
22764                 
22765                 if(isNaN(this.vIndex)){
22766                     this.vIndex = 0;
22767                 }
22768                 
22769                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22770                 
22771                 break;
22772             case 38: // up
22773             case 40: // down
22774                 
22775                 dir = e.keyCode == 38 ? -1 : 1;
22776                 
22777                 this.vIndex = this.vIndex + dir * 4;
22778                 
22779                 if(this.vIndex < 0){
22780                     this.vIndex = 0;
22781                 }
22782                 
22783                 if(this.vIndex > 11){
22784                     this.vIndex = 11;
22785                 }
22786                 
22787                 if(isNaN(this.vIndex)){
22788                     this.vIndex = 0;
22789                 }
22790                 
22791                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22792                 break;
22793                 
22794             case 13: // enter
22795                 
22796                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22797                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22798                 }
22799                 
22800                 this.hide();
22801                 e.preventDefault();
22802                 break;
22803             case 9: // tab
22804                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22805                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22806                 }
22807                 this.hide();
22808                 break;
22809             case 16: // shift
22810             case 17: // ctrl
22811             case 18: // alt
22812                 break;
22813             default :
22814                 this.hide();
22815                 
22816         }
22817     },
22818     
22819     remove: function() 
22820     {
22821         this.picker().remove();
22822     }
22823    
22824 });
22825
22826 Roo.apply(Roo.bootstrap.MonthField,  {
22827     
22828     content : {
22829         tag: 'tbody',
22830         cn: [
22831         {
22832             tag: 'tr',
22833             cn: [
22834             {
22835                 tag: 'td',
22836                 colspan: '7'
22837             }
22838             ]
22839         }
22840         ]
22841     },
22842     
22843     dates:{
22844         en: {
22845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22847         }
22848     }
22849 });
22850
22851 Roo.apply(Roo.bootstrap.MonthField,  {
22852   
22853     template : {
22854         tag: 'div',
22855         cls: 'datepicker dropdown-menu roo-dynamic',
22856         cn: [
22857             {
22858                 tag: 'div',
22859                 cls: 'datepicker-months',
22860                 cn: [
22861                 {
22862                     tag: 'table',
22863                     cls: 'table-condensed',
22864                     cn:[
22865                         Roo.bootstrap.DateField.content
22866                     ]
22867                 }
22868                 ]
22869             }
22870         ]
22871     }
22872 });
22873
22874  
22875
22876  
22877  /*
22878  * - LGPL
22879  *
22880  * CheckBox
22881  * 
22882  */
22883
22884 /**
22885  * @class Roo.bootstrap.CheckBox
22886  * @extends Roo.bootstrap.Input
22887  * Bootstrap CheckBox class
22888  * 
22889  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22890  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22891  * @cfg {String} boxLabel The text that appears beside the checkbox
22892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22893  * @cfg {Boolean} checked initnal the element
22894  * @cfg {Boolean} inline inline the element (default false)
22895  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22896  * @cfg {String} tooltip label tooltip
22897  * 
22898  * @constructor
22899  * Create a new CheckBox
22900  * @param {Object} config The config object
22901  */
22902
22903 Roo.bootstrap.CheckBox = function(config){
22904     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22905    
22906     this.addEvents({
22907         /**
22908         * @event check
22909         * Fires when the element is checked or unchecked.
22910         * @param {Roo.bootstrap.CheckBox} this This input
22911         * @param {Boolean} checked The new checked value
22912         */
22913        check : true,
22914        /**
22915         * @event click
22916         * Fires when the element is click.
22917         * @param {Roo.bootstrap.CheckBox} this This input
22918         */
22919        click : true
22920     });
22921     
22922 };
22923
22924 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22925   
22926     inputType: 'checkbox',
22927     inputValue: 1,
22928     valueOff: 0,
22929     boxLabel: false,
22930     checked: false,
22931     weight : false,
22932     inline: false,
22933     tooltip : '',
22934     
22935     // checkbox success does not make any sense really.. 
22936     invalidClass : "",
22937     validClass : "",
22938     
22939     
22940     getAutoCreate : function()
22941     {
22942         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22943         
22944         var id = Roo.id();
22945         
22946         var cfg = {};
22947         
22948         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22949         
22950         if(this.inline){
22951             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22952         }
22953         
22954         var input =  {
22955             tag: 'input',
22956             id : id,
22957             type : this.inputType,
22958             value : this.inputValue,
22959             cls : 'roo-' + this.inputType, //'form-box',
22960             placeholder : this.placeholder || ''
22961             
22962         };
22963         
22964         if(this.inputType != 'radio'){
22965             var hidden =  {
22966                 tag: 'input',
22967                 type : 'hidden',
22968                 cls : 'roo-hidden-value',
22969                 value : this.checked ? this.inputValue : this.valueOff
22970             };
22971         }
22972         
22973             
22974         if (this.weight) { // Validity check?
22975             cfg.cls += " " + this.inputType + "-" + this.weight;
22976         }
22977         
22978         if (this.disabled) {
22979             input.disabled=true;
22980         }
22981         
22982         if(this.checked){
22983             input.checked = this.checked;
22984         }
22985         
22986         if (this.name) {
22987             
22988             input.name = this.name;
22989             
22990             if(this.inputType != 'radio'){
22991                 hidden.name = this.name;
22992                 input.name = '_hidden_' + this.name;
22993             }
22994         }
22995         
22996         if (this.size) {
22997             input.cls += ' input-' + this.size;
22998         }
22999         
23000         var settings=this;
23001         
23002         ['xs','sm','md','lg'].map(function(size){
23003             if (settings[size]) {
23004                 cfg.cls += ' col-' + size + '-' + settings[size];
23005             }
23006         });
23007         
23008         var inputblock = input;
23009          
23010         if (this.before || this.after) {
23011             
23012             inputblock = {
23013                 cls : 'input-group',
23014                 cn :  [] 
23015             };
23016             
23017             if (this.before) {
23018                 inputblock.cn.push({
23019                     tag :'span',
23020                     cls : 'input-group-addon',
23021                     html : this.before
23022                 });
23023             }
23024             
23025             inputblock.cn.push(input);
23026             
23027             if(this.inputType != 'radio'){
23028                 inputblock.cn.push(hidden);
23029             }
23030             
23031             if (this.after) {
23032                 inputblock.cn.push({
23033                     tag :'span',
23034                     cls : 'input-group-addon',
23035                     html : this.after
23036                 });
23037             }
23038             
23039         }
23040         var boxLabelCfg = false;
23041         
23042         if(this.boxLabel){
23043            
23044             boxLabelCfg = {
23045                 tag: 'label',
23046                 //'for': id, // box label is handled by onclick - so no for...
23047                 cls: 'box-label',
23048                 html: this.boxLabel
23049             };
23050             if(this.tooltip){
23051                 boxLabelCfg.tooltip = this.tooltip;
23052             }
23053              
23054         }
23055         
23056         
23057         if (align ==='left' && this.fieldLabel.length) {
23058 //                Roo.log("left and has label");
23059             cfg.cn = [
23060                 {
23061                     tag: 'label',
23062                     'for' :  id,
23063                     cls : 'control-label',
23064                     html : this.fieldLabel
23065                 },
23066                 {
23067                     cls : "", 
23068                     cn: [
23069                         inputblock
23070                     ]
23071                 }
23072             ];
23073             
23074             if (boxLabelCfg) {
23075                 cfg.cn[1].cn.push(boxLabelCfg);
23076             }
23077             
23078             if(this.labelWidth > 12){
23079                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23080             }
23081             
23082             if(this.labelWidth < 13 && this.labelmd == 0){
23083                 this.labelmd = this.labelWidth;
23084             }
23085             
23086             if(this.labellg > 0){
23087                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23088                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23089             }
23090             
23091             if(this.labelmd > 0){
23092                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23093                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23094             }
23095             
23096             if(this.labelsm > 0){
23097                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23098                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23099             }
23100             
23101             if(this.labelxs > 0){
23102                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23103                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23104             }
23105             
23106         } else if ( this.fieldLabel.length) {
23107 //                Roo.log(" label");
23108                 cfg.cn = [
23109                    
23110                     {
23111                         tag: this.boxLabel ? 'span' : 'label',
23112                         'for': id,
23113                         cls: 'control-label box-input-label',
23114                         //cls : 'input-group-addon',
23115                         html : this.fieldLabel
23116                     },
23117                     
23118                     inputblock
23119                     
23120                 ];
23121                 if (boxLabelCfg) {
23122                     cfg.cn.push(boxLabelCfg);
23123                 }
23124
23125         } else {
23126             
23127 //                Roo.log(" no label && no align");
23128                 cfg.cn = [  inputblock ] ;
23129                 if (boxLabelCfg) {
23130                     cfg.cn.push(boxLabelCfg);
23131                 }
23132
23133                 
23134         }
23135         
23136        
23137         
23138         if(this.inputType != 'radio'){
23139             cfg.cn.push(hidden);
23140         }
23141         
23142         return cfg;
23143         
23144     },
23145     
23146     /**
23147      * return the real input element.
23148      */
23149     inputEl: function ()
23150     {
23151         return this.el.select('input.roo-' + this.inputType,true).first();
23152     },
23153     hiddenEl: function ()
23154     {
23155         return this.el.select('input.roo-hidden-value',true).first();
23156     },
23157     
23158     labelEl: function()
23159     {
23160         return this.el.select('label.control-label',true).first();
23161     },
23162     /* depricated... */
23163     
23164     label: function()
23165     {
23166         return this.labelEl();
23167     },
23168     
23169     boxLabelEl: function()
23170     {
23171         return this.el.select('label.box-label',true).first();
23172     },
23173     
23174     initEvents : function()
23175     {
23176 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23177         
23178         this.inputEl().on('click', this.onClick,  this);
23179         
23180         if (this.boxLabel) { 
23181             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23182         }
23183         
23184         this.startValue = this.getValue();
23185         
23186         if(this.groupId){
23187             Roo.bootstrap.CheckBox.register(this);
23188         }
23189     },
23190     
23191     onClick : function(e)
23192     {   
23193         if(this.fireEvent('click', this, e) !== false){
23194             this.setChecked(!this.checked);
23195         }
23196         
23197     },
23198     
23199     setChecked : function(state,suppressEvent)
23200     {
23201         this.startValue = this.getValue();
23202
23203         if(this.inputType == 'radio'){
23204             
23205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23206                 e.dom.checked = false;
23207             });
23208             
23209             this.inputEl().dom.checked = true;
23210             
23211             this.inputEl().dom.value = this.inputValue;
23212             
23213             if(suppressEvent !== true){
23214                 this.fireEvent('check', this, true);
23215             }
23216             
23217             this.validate();
23218             
23219             return;
23220         }
23221         
23222         this.checked = state;
23223         
23224         this.inputEl().dom.checked = state;
23225         
23226         
23227         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23228         
23229         if(suppressEvent !== true){
23230             this.fireEvent('check', this, state);
23231         }
23232         
23233         this.validate();
23234     },
23235     
23236     getValue : function()
23237     {
23238         if(this.inputType == 'radio'){
23239             return this.getGroupValue();
23240         }
23241         
23242         return this.hiddenEl().dom.value;
23243         
23244     },
23245     
23246     getGroupValue : function()
23247     {
23248         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23249             return '';
23250         }
23251         
23252         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23253     },
23254     
23255     setValue : function(v,suppressEvent)
23256     {
23257         if(this.inputType == 'radio'){
23258             this.setGroupValue(v, suppressEvent);
23259             return;
23260         }
23261         
23262         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23263         
23264         this.validate();
23265     },
23266     
23267     setGroupValue : function(v, suppressEvent)
23268     {
23269         this.startValue = this.getValue();
23270         
23271         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23272             e.dom.checked = false;
23273             
23274             if(e.dom.value == v){
23275                 e.dom.checked = true;
23276             }
23277         });
23278         
23279         if(suppressEvent !== true){
23280             this.fireEvent('check', this, true);
23281         }
23282
23283         this.validate();
23284         
23285         return;
23286     },
23287     
23288     validate : function()
23289     {
23290         if(this.getVisibilityEl().hasClass('hidden')){
23291             return true;
23292         }
23293         
23294         if(
23295                 this.disabled || 
23296                 (this.inputType == 'radio' && this.validateRadio()) ||
23297                 (this.inputType == 'checkbox' && this.validateCheckbox())
23298         ){
23299             this.markValid();
23300             return true;
23301         }
23302         
23303         this.markInvalid();
23304         return false;
23305     },
23306     
23307     validateRadio : function()
23308     {
23309         if(this.getVisibilityEl().hasClass('hidden')){
23310             return true;
23311         }
23312         
23313         if(this.allowBlank){
23314             return true;
23315         }
23316         
23317         var valid = false;
23318         
23319         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23320             if(!e.dom.checked){
23321                 return;
23322             }
23323             
23324             valid = true;
23325             
23326             return false;
23327         });
23328         
23329         return valid;
23330     },
23331     
23332     validateCheckbox : function()
23333     {
23334         if(!this.groupId){
23335             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23336             //return (this.getValue() == this.inputValue) ? true : false;
23337         }
23338         
23339         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23340         
23341         if(!group){
23342             return false;
23343         }
23344         
23345         var r = false;
23346         
23347         for(var i in group){
23348             if(group[i].el.isVisible(true)){
23349                 r = false;
23350                 break;
23351             }
23352             
23353             r = true;
23354         }
23355         
23356         for(var i in group){
23357             if(r){
23358                 break;
23359             }
23360             
23361             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23362         }
23363         
23364         return r;
23365     },
23366     
23367     /**
23368      * Mark this field as valid
23369      */
23370     markValid : function()
23371     {
23372         var _this = this;
23373         
23374         this.fireEvent('valid', this);
23375         
23376         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23377         
23378         if(this.groupId){
23379             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23380         }
23381         
23382         if(label){
23383             label.markValid();
23384         }
23385
23386         if(this.inputType == 'radio'){
23387             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23388                 var fg = e.findParent('.form-group', false, true);
23389                 if (Roo.bootstrap.version == 3) {
23390                     fg.removeClass([_this.invalidClass, _this.validClass]);
23391                     fg.addClass(_this.validClass);
23392                 } else {
23393                     fg.removeClass(['is-valid', 'is-invalid']);
23394                     fg.addClass('is-valid');
23395                 }
23396             });
23397             
23398             return;
23399         }
23400
23401         if(!this.groupId){
23402             var fg = this.el.findParent('.form-group', false, true);
23403             if (Roo.bootstrap.version == 3) {
23404                 fg.removeClass([this.invalidClass, this.validClass]);
23405                 fg.addClass(this.validClass);
23406             } else {
23407                 fg.removeClass(['is-valid', 'is-invalid']);
23408                 fg.addClass('is-valid');
23409             }
23410             return;
23411         }
23412         
23413         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23414         
23415         if(!group){
23416             return;
23417         }
23418         
23419         for(var i in group){
23420             var fg = group[i].el.findParent('.form-group', false, true);
23421             if (Roo.bootstrap.version == 3) {
23422                 fg.removeClass([this.invalidClass, this.validClass]);
23423                 fg.addClass(this.validClass);
23424             } else {
23425                 fg.removeClass(['is-valid', 'is-invalid']);
23426                 fg.addClass('is-valid');
23427             }
23428         }
23429     },
23430     
23431      /**
23432      * Mark this field as invalid
23433      * @param {String} msg The validation message
23434      */
23435     markInvalid : function(msg)
23436     {
23437         if(this.allowBlank){
23438             return;
23439         }
23440         
23441         var _this = this;
23442         
23443         this.fireEvent('invalid', this, msg);
23444         
23445         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23446         
23447         if(this.groupId){
23448             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23449         }
23450         
23451         if(label){
23452             label.markInvalid();
23453         }
23454             
23455         if(this.inputType == 'radio'){
23456             
23457             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23458                 var fg = e.findParent('.form-group', false, true);
23459                 if (Roo.bootstrap.version == 3) {
23460                     fg.removeClass([_this.invalidClass, _this.validClass]);
23461                     fg.addClass(_this.invalidClass);
23462                 } else {
23463                     fg.removeClass(['is-invalid', 'is-valid']);
23464                     fg.addClass('is-invalid');
23465                 }
23466             });
23467             
23468             return;
23469         }
23470         
23471         if(!this.groupId){
23472             var fg = this.el.findParent('.form-group', false, true);
23473             if (Roo.bootstrap.version == 3) {
23474                 fg.removeClass([_this.invalidClass, _this.validClass]);
23475                 fg.addClass(_this.invalidClass);
23476             } else {
23477                 fg.removeClass(['is-invalid', 'is-valid']);
23478                 fg.addClass('is-invalid');
23479             }
23480             return;
23481         }
23482         
23483         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23484         
23485         if(!group){
23486             return;
23487         }
23488         
23489         for(var i in group){
23490             var fg = group[i].el.findParent('.form-group', false, true);
23491             if (Roo.bootstrap.version == 3) {
23492                 fg.removeClass([_this.invalidClass, _this.validClass]);
23493                 fg.addClass(_this.invalidClass);
23494             } else {
23495                 fg.removeClass(['is-invalid', 'is-valid']);
23496                 fg.addClass('is-invalid');
23497             }
23498         }
23499         
23500     },
23501     
23502     clearInvalid : function()
23503     {
23504         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23505         
23506         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23507         
23508         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23509         
23510         if (label && label.iconEl) {
23511             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23512             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23513         }
23514     },
23515     
23516     disable : function()
23517     {
23518         if(this.inputType != 'radio'){
23519             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23520             return;
23521         }
23522         
23523         var _this = this;
23524         
23525         if(this.rendered){
23526             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23527                 _this.getActionEl().addClass(this.disabledClass);
23528                 e.dom.disabled = true;
23529             });
23530         }
23531         
23532         this.disabled = true;
23533         this.fireEvent("disable", this);
23534         return this;
23535     },
23536
23537     enable : function()
23538     {
23539         if(this.inputType != 'radio'){
23540             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23541             return;
23542         }
23543         
23544         var _this = this;
23545         
23546         if(this.rendered){
23547             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23548                 _this.getActionEl().removeClass(this.disabledClass);
23549                 e.dom.disabled = false;
23550             });
23551         }
23552         
23553         this.disabled = false;
23554         this.fireEvent("enable", this);
23555         return this;
23556     },
23557     
23558     setBoxLabel : function(v)
23559     {
23560         this.boxLabel = v;
23561         
23562         if(this.rendered){
23563             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23564         }
23565     }
23566
23567 });
23568
23569 Roo.apply(Roo.bootstrap.CheckBox, {
23570     
23571     groups: {},
23572     
23573      /**
23574     * register a CheckBox Group
23575     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23576     */
23577     register : function(checkbox)
23578     {
23579         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23580             this.groups[checkbox.groupId] = {};
23581         }
23582         
23583         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23584             return;
23585         }
23586         
23587         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23588         
23589     },
23590     /**
23591     * fetch a CheckBox Group based on the group ID
23592     * @param {string} the group ID
23593     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23594     */
23595     get: function(groupId) {
23596         if (typeof(this.groups[groupId]) == 'undefined') {
23597             return false;
23598         }
23599         
23600         return this.groups[groupId] ;
23601     }
23602     
23603     
23604 });
23605 /*
23606  * - LGPL
23607  *
23608  * RadioItem
23609  * 
23610  */
23611
23612 /**
23613  * @class Roo.bootstrap.Radio
23614  * @extends Roo.bootstrap.Component
23615  * Bootstrap Radio class
23616  * @cfg {String} boxLabel - the label associated
23617  * @cfg {String} value - the value of radio
23618  * 
23619  * @constructor
23620  * Create a new Radio
23621  * @param {Object} config The config object
23622  */
23623 Roo.bootstrap.Radio = function(config){
23624     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23625     
23626 };
23627
23628 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23629     
23630     boxLabel : '',
23631     
23632     value : '',
23633     
23634     getAutoCreate : function()
23635     {
23636         var cfg = {
23637             tag : 'div',
23638             cls : 'form-group radio',
23639             cn : [
23640                 {
23641                     tag : 'label',
23642                     cls : 'box-label',
23643                     html : this.boxLabel
23644                 }
23645             ]
23646         };
23647         
23648         return cfg;
23649     },
23650     
23651     initEvents : function() 
23652     {
23653         this.parent().register(this);
23654         
23655         this.el.on('click', this.onClick, this);
23656         
23657     },
23658     
23659     onClick : function(e)
23660     {
23661         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23662             this.setChecked(true);
23663         }
23664     },
23665     
23666     setChecked : function(state, suppressEvent)
23667     {
23668         this.parent().setValue(this.value, suppressEvent);
23669         
23670     },
23671     
23672     setBoxLabel : function(v)
23673     {
23674         this.boxLabel = v;
23675         
23676         if(this.rendered){
23677             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23678         }
23679     }
23680     
23681 });
23682  
23683
23684  /*
23685  * - LGPL
23686  *
23687  * Input
23688  * 
23689  */
23690
23691 /**
23692  * @class Roo.bootstrap.SecurePass
23693  * @extends Roo.bootstrap.Input
23694  * Bootstrap SecurePass class
23695  *
23696  * 
23697  * @constructor
23698  * Create a new SecurePass
23699  * @param {Object} config The config object
23700  */
23701  
23702 Roo.bootstrap.SecurePass = function (config) {
23703     // these go here, so the translation tool can replace them..
23704     this.errors = {
23705         PwdEmpty: "Please type a password, and then retype it to confirm.",
23706         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23707         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23708         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23709         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23710         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23711         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23712         TooWeak: "Your password is Too Weak."
23713     },
23714     this.meterLabel = "Password strength:";
23715     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23716     this.meterClass = [
23717         "roo-password-meter-tooweak", 
23718         "roo-password-meter-weak", 
23719         "roo-password-meter-medium", 
23720         "roo-password-meter-strong", 
23721         "roo-password-meter-grey"
23722     ];
23723     
23724     this.errors = {};
23725     
23726     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23727 }
23728
23729 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23730     /**
23731      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23732      * {
23733      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23734      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23735      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23736      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23737      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23738      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23739      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23740      * })
23741      */
23742     // private
23743     
23744     meterWidth: 300,
23745     errorMsg :'',    
23746     errors: false,
23747     imageRoot: '/',
23748     /**
23749      * @cfg {String/Object} Label for the strength meter (defaults to
23750      * 'Password strength:')
23751      */
23752     // private
23753     meterLabel: '',
23754     /**
23755      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23756      * ['Weak', 'Medium', 'Strong'])
23757      */
23758     // private    
23759     pwdStrengths: false,    
23760     // private
23761     strength: 0,
23762     // private
23763     _lastPwd: null,
23764     // private
23765     kCapitalLetter: 0,
23766     kSmallLetter: 1,
23767     kDigit: 2,
23768     kPunctuation: 3,
23769     
23770     insecure: false,
23771     // private
23772     initEvents: function ()
23773     {
23774         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23775
23776         if (this.el.is('input[type=password]') && Roo.isSafari) {
23777             this.el.on('keydown', this.SafariOnKeyDown, this);
23778         }
23779
23780         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23781     },
23782     // private
23783     onRender: function (ct, position)
23784     {
23785         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23786         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23787         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23788
23789         this.trigger.createChild({
23790                    cn: [
23791                     {
23792                     //id: 'PwdMeter',
23793                     tag: 'div',
23794                     cls: 'roo-password-meter-grey col-xs-12',
23795                     style: {
23796                         //width: 0,
23797                         //width: this.meterWidth + 'px'                                                
23798                         }
23799                     },
23800                     {                            
23801                          cls: 'roo-password-meter-text'                          
23802                     }
23803                 ]            
23804         });
23805
23806          
23807         if (this.hideTrigger) {
23808             this.trigger.setDisplayed(false);
23809         }
23810         this.setSize(this.width || '', this.height || '');
23811     },
23812     // private
23813     onDestroy: function ()
23814     {
23815         if (this.trigger) {
23816             this.trigger.removeAllListeners();
23817             this.trigger.remove();
23818         }
23819         if (this.wrap) {
23820             this.wrap.remove();
23821         }
23822         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23823     },
23824     // private
23825     checkStrength: function ()
23826     {
23827         var pwd = this.inputEl().getValue();
23828         if (pwd == this._lastPwd) {
23829             return;
23830         }
23831
23832         var strength;
23833         if (this.ClientSideStrongPassword(pwd)) {
23834             strength = 3;
23835         } else if (this.ClientSideMediumPassword(pwd)) {
23836             strength = 2;
23837         } else if (this.ClientSideWeakPassword(pwd)) {
23838             strength = 1;
23839         } else {
23840             strength = 0;
23841         }
23842         
23843         Roo.log('strength1: ' + strength);
23844         
23845         //var pm = this.trigger.child('div/div/div').dom;
23846         var pm = this.trigger.child('div/div');
23847         pm.removeClass(this.meterClass);
23848         pm.addClass(this.meterClass[strength]);
23849                 
23850         
23851         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23852                 
23853         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23854         
23855         this._lastPwd = pwd;
23856     },
23857     reset: function ()
23858     {
23859         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23860         
23861         this._lastPwd = '';
23862         
23863         var pm = this.trigger.child('div/div');
23864         pm.removeClass(this.meterClass);
23865         pm.addClass('roo-password-meter-grey');        
23866         
23867         
23868         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23869         
23870         pt.innerHTML = '';
23871         this.inputEl().dom.type='password';
23872     },
23873     // private
23874     validateValue: function (value)
23875     {
23876         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23877             return false;
23878         }
23879         if (value.length == 0) {
23880             if (this.allowBlank) {
23881                 this.clearInvalid();
23882                 return true;
23883             }
23884
23885             this.markInvalid(this.errors.PwdEmpty);
23886             this.errorMsg = this.errors.PwdEmpty;
23887             return false;
23888         }
23889         
23890         if(this.insecure){
23891             return true;
23892         }
23893         
23894         if (!value.match(/[\x21-\x7e]+/)) {
23895             this.markInvalid(this.errors.PwdBadChar);
23896             this.errorMsg = this.errors.PwdBadChar;
23897             return false;
23898         }
23899         if (value.length < 6) {
23900             this.markInvalid(this.errors.PwdShort);
23901             this.errorMsg = this.errors.PwdShort;
23902             return false;
23903         }
23904         if (value.length > 16) {
23905             this.markInvalid(this.errors.PwdLong);
23906             this.errorMsg = this.errors.PwdLong;
23907             return false;
23908         }
23909         var strength;
23910         if (this.ClientSideStrongPassword(value)) {
23911             strength = 3;
23912         } else if (this.ClientSideMediumPassword(value)) {
23913             strength = 2;
23914         } else if (this.ClientSideWeakPassword(value)) {
23915             strength = 1;
23916         } else {
23917             strength = 0;
23918         }
23919
23920         
23921         if (strength < 2) {
23922             //this.markInvalid(this.errors.TooWeak);
23923             this.errorMsg = this.errors.TooWeak;
23924             //return false;
23925         }
23926         
23927         
23928         console.log('strength2: ' + strength);
23929         
23930         //var pm = this.trigger.child('div/div/div').dom;
23931         
23932         var pm = this.trigger.child('div/div');
23933         pm.removeClass(this.meterClass);
23934         pm.addClass(this.meterClass[strength]);
23935                 
23936         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23937                 
23938         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23939         
23940         this.errorMsg = ''; 
23941         return true;
23942     },
23943     // private
23944     CharacterSetChecks: function (type)
23945     {
23946         this.type = type;
23947         this.fResult = false;
23948     },
23949     // private
23950     isctype: function (character, type)
23951     {
23952         switch (type) {  
23953             case this.kCapitalLetter:
23954                 if (character >= 'A' && character <= 'Z') {
23955                     return true;
23956                 }
23957                 break;
23958             
23959             case this.kSmallLetter:
23960                 if (character >= 'a' && character <= 'z') {
23961                     return true;
23962                 }
23963                 break;
23964             
23965             case this.kDigit:
23966                 if (character >= '0' && character <= '9') {
23967                     return true;
23968                 }
23969                 break;
23970             
23971             case this.kPunctuation:
23972                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23973                     return true;
23974                 }
23975                 break;
23976             
23977             default:
23978                 return false;
23979         }
23980
23981     },
23982     // private
23983     IsLongEnough: function (pwd, size)
23984     {
23985         return !(pwd == null || isNaN(size) || pwd.length < size);
23986     },
23987     // private
23988     SpansEnoughCharacterSets: function (word, nb)
23989     {
23990         if (!this.IsLongEnough(word, nb))
23991         {
23992             return false;
23993         }
23994
23995         var characterSetChecks = new Array(
23996             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23997             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23998         );
23999         
24000         for (var index = 0; index < word.length; ++index) {
24001             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24002                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24003                     characterSetChecks[nCharSet].fResult = true;
24004                     break;
24005                 }
24006             }
24007         }
24008
24009         var nCharSets = 0;
24010         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24011             if (characterSetChecks[nCharSet].fResult) {
24012                 ++nCharSets;
24013             }
24014         }
24015
24016         if (nCharSets < nb) {
24017             return false;
24018         }
24019         return true;
24020     },
24021     // private
24022     ClientSideStrongPassword: function (pwd)
24023     {
24024         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24025     },
24026     // private
24027     ClientSideMediumPassword: function (pwd)
24028     {
24029         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24030     },
24031     // private
24032     ClientSideWeakPassword: function (pwd)
24033     {
24034         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24035     }
24036           
24037 })//<script type="text/javascript">
24038
24039 /*
24040  * Based  Ext JS Library 1.1.1
24041  * Copyright(c) 2006-2007, Ext JS, LLC.
24042  * LGPL
24043  *
24044  */
24045  
24046 /**
24047  * @class Roo.HtmlEditorCore
24048  * @extends Roo.Component
24049  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24050  *
24051  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24052  */
24053
24054 Roo.HtmlEditorCore = function(config){
24055     
24056     
24057     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24058     
24059     
24060     this.addEvents({
24061         /**
24062          * @event initialize
24063          * Fires when the editor is fully initialized (including the iframe)
24064          * @param {Roo.HtmlEditorCore} this
24065          */
24066         initialize: true,
24067         /**
24068          * @event activate
24069          * Fires when the editor is first receives the focus. Any insertion must wait
24070          * until after this event.
24071          * @param {Roo.HtmlEditorCore} this
24072          */
24073         activate: true,
24074          /**
24075          * @event beforesync
24076          * Fires before the textarea is updated with content from the editor iframe. Return false
24077          * to cancel the sync.
24078          * @param {Roo.HtmlEditorCore} this
24079          * @param {String} html
24080          */
24081         beforesync: true,
24082          /**
24083          * @event beforepush
24084          * Fires before the iframe editor is updated with content from the textarea. Return false
24085          * to cancel the push.
24086          * @param {Roo.HtmlEditorCore} this
24087          * @param {String} html
24088          */
24089         beforepush: true,
24090          /**
24091          * @event sync
24092          * Fires when the textarea is updated with content from the editor iframe.
24093          * @param {Roo.HtmlEditorCore} this
24094          * @param {String} html
24095          */
24096         sync: true,
24097          /**
24098          * @event push
24099          * Fires when the iframe editor is updated with content from the textarea.
24100          * @param {Roo.HtmlEditorCore} this
24101          * @param {String} html
24102          */
24103         push: true,
24104         
24105         /**
24106          * @event editorevent
24107          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24108          * @param {Roo.HtmlEditorCore} this
24109          */
24110         editorevent: true
24111         
24112     });
24113     
24114     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24115     
24116     // defaults : white / black...
24117     this.applyBlacklists();
24118     
24119     
24120     
24121 };
24122
24123
24124 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24125
24126
24127      /**
24128      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24129      */
24130     
24131     owner : false,
24132     
24133      /**
24134      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24135      *                        Roo.resizable.
24136      */
24137     resizable : false,
24138      /**
24139      * @cfg {Number} height (in pixels)
24140      */   
24141     height: 300,
24142    /**
24143      * @cfg {Number} width (in pixels)
24144      */   
24145     width: 500,
24146     
24147     /**
24148      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24149      * 
24150      */
24151     stylesheets: false,
24152     
24153     // id of frame..
24154     frameId: false,
24155     
24156     // private properties
24157     validationEvent : false,
24158     deferHeight: true,
24159     initialized : false,
24160     activated : false,
24161     sourceEditMode : false,
24162     onFocus : Roo.emptyFn,
24163     iframePad:3,
24164     hideMode:'offsets',
24165     
24166     clearUp: true,
24167     
24168     // blacklist + whitelisted elements..
24169     black: false,
24170     white: false,
24171      
24172     bodyCls : '',
24173
24174     /**
24175      * Protected method that will not generally be called directly. It
24176      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24177      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24178      */
24179     getDocMarkup : function(){
24180         // body styles..
24181         var st = '';
24182         
24183         // inherit styels from page...?? 
24184         if (this.stylesheets === false) {
24185             
24186             Roo.get(document.head).select('style').each(function(node) {
24187                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24188             });
24189             
24190             Roo.get(document.head).select('link').each(function(node) { 
24191                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24192             });
24193             
24194         } else if (!this.stylesheets.length) {
24195                 // simple..
24196                 st = '<style type="text/css">' +
24197                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24198                    '</style>';
24199         } else {
24200             for (var i in this.stylesheets) { 
24201                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24202             }
24203             
24204         }
24205         
24206         st +=  '<style type="text/css">' +
24207             'IMG { cursor: pointer } ' +
24208         '</style>';
24209
24210         var cls = 'roo-htmleditor-body';
24211         
24212         if(this.bodyCls.length){
24213             cls += ' ' + this.bodyCls;
24214         }
24215         
24216         return '<html><head>' + st  +
24217             //<style type="text/css">' +
24218             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24219             //'</style>' +
24220             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24221     },
24222
24223     // private
24224     onRender : function(ct, position)
24225     {
24226         var _t = this;
24227         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24228         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24229         
24230         
24231         this.el.dom.style.border = '0 none';
24232         this.el.dom.setAttribute('tabIndex', -1);
24233         this.el.addClass('x-hidden hide');
24234         
24235         
24236         
24237         if(Roo.isIE){ // fix IE 1px bogus margin
24238             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24239         }
24240        
24241         
24242         this.frameId = Roo.id();
24243         
24244          
24245         
24246         var iframe = this.owner.wrap.createChild({
24247             tag: 'iframe',
24248             cls: 'form-control', // bootstrap..
24249             id: this.frameId,
24250             name: this.frameId,
24251             frameBorder : 'no',
24252             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24253         }, this.el
24254         );
24255         
24256         
24257         this.iframe = iframe.dom;
24258
24259          this.assignDocWin();
24260         
24261         this.doc.designMode = 'on';
24262        
24263         this.doc.open();
24264         this.doc.write(this.getDocMarkup());
24265         this.doc.close();
24266
24267         
24268         var task = { // must defer to wait for browser to be ready
24269             run : function(){
24270                 //console.log("run task?" + this.doc.readyState);
24271                 this.assignDocWin();
24272                 if(this.doc.body || this.doc.readyState == 'complete'){
24273                     try {
24274                         this.doc.designMode="on";
24275                     } catch (e) {
24276                         return;
24277                     }
24278                     Roo.TaskMgr.stop(task);
24279                     this.initEditor.defer(10, this);
24280                 }
24281             },
24282             interval : 10,
24283             duration: 10000,
24284             scope: this
24285         };
24286         Roo.TaskMgr.start(task);
24287
24288     },
24289
24290     // private
24291     onResize : function(w, h)
24292     {
24293          Roo.log('resize: ' +w + ',' + h );
24294         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24295         if(!this.iframe){
24296             return;
24297         }
24298         if(typeof w == 'number'){
24299             
24300             this.iframe.style.width = w + 'px';
24301         }
24302         if(typeof h == 'number'){
24303             
24304             this.iframe.style.height = h + 'px';
24305             if(this.doc){
24306                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24307             }
24308         }
24309         
24310     },
24311
24312     /**
24313      * Toggles the editor between standard and source edit mode.
24314      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24315      */
24316     toggleSourceEdit : function(sourceEditMode){
24317         
24318         this.sourceEditMode = sourceEditMode === true;
24319         
24320         if(this.sourceEditMode){
24321  
24322             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24323             
24324         }else{
24325             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24326             //this.iframe.className = '';
24327             this.deferFocus();
24328         }
24329         //this.setSize(this.owner.wrap.getSize());
24330         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24331     },
24332
24333     
24334   
24335
24336     /**
24337      * Protected method that will not generally be called directly. If you need/want
24338      * custom HTML cleanup, this is the method you should override.
24339      * @param {String} html The HTML to be cleaned
24340      * return {String} The cleaned HTML
24341      */
24342     cleanHtml : function(html){
24343         html = String(html);
24344         if(html.length > 5){
24345             if(Roo.isSafari){ // strip safari nonsense
24346                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24347             }
24348         }
24349         if(html == '&nbsp;'){
24350             html = '';
24351         }
24352         return html;
24353     },
24354
24355     /**
24356      * HTML Editor -> Textarea
24357      * Protected method that will not generally be called directly. Syncs the contents
24358      * of the editor iframe with the textarea.
24359      */
24360     syncValue : function(){
24361         if(this.initialized){
24362             var bd = (this.doc.body || this.doc.documentElement);
24363             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24364             var html = bd.innerHTML;
24365             if(Roo.isSafari){
24366                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24367                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24368                 if(m && m[1]){
24369                     html = '<div style="'+m[0]+'">' + html + '</div>';
24370                 }
24371             }
24372             html = this.cleanHtml(html);
24373             // fix up the special chars.. normaly like back quotes in word...
24374             // however we do not want to do this with chinese..
24375             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24376                 
24377                 var cc = match.charCodeAt();
24378
24379                 // Get the character value, handling surrogate pairs
24380                 if (match.length == 2) {
24381                     // It's a surrogate pair, calculate the Unicode code point
24382                     var high = match.charCodeAt(0) - 0xD800;
24383                     var low  = match.charCodeAt(1) - 0xDC00;
24384                     cc = (high * 0x400) + low + 0x10000;
24385                 }  else if (
24386                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24387                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24388                     (cc >= 0xf900 && cc < 0xfb00 )
24389                 ) {
24390                         return match;
24391                 }  
24392          
24393                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24394                 return "&#" + cc + ";";
24395                 
24396                 
24397             });
24398             
24399             
24400              
24401             if(this.owner.fireEvent('beforesync', this, html) !== false){
24402                 this.el.dom.value = html;
24403                 this.owner.fireEvent('sync', this, html);
24404             }
24405         }
24406     },
24407
24408     /**
24409      * Protected method that will not generally be called directly. Pushes the value of the textarea
24410      * into the iframe editor.
24411      */
24412     pushValue : function(){
24413         if(this.initialized){
24414             var v = this.el.dom.value.trim();
24415             
24416 //            if(v.length < 1){
24417 //                v = '&#160;';
24418 //            }
24419             
24420             if(this.owner.fireEvent('beforepush', this, v) !== false){
24421                 var d = (this.doc.body || this.doc.documentElement);
24422                 d.innerHTML = v;
24423                 this.cleanUpPaste();
24424                 this.el.dom.value = d.innerHTML;
24425                 this.owner.fireEvent('push', this, v);
24426             }
24427         }
24428     },
24429
24430     // private
24431     deferFocus : function(){
24432         this.focus.defer(10, this);
24433     },
24434
24435     // doc'ed in Field
24436     focus : function(){
24437         if(this.win && !this.sourceEditMode){
24438             this.win.focus();
24439         }else{
24440             this.el.focus();
24441         }
24442     },
24443     
24444     assignDocWin: function()
24445     {
24446         var iframe = this.iframe;
24447         
24448          if(Roo.isIE){
24449             this.doc = iframe.contentWindow.document;
24450             this.win = iframe.contentWindow;
24451         } else {
24452 //            if (!Roo.get(this.frameId)) {
24453 //                return;
24454 //            }
24455 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24456 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24457             
24458             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24459                 return;
24460             }
24461             
24462             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24463             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24464         }
24465     },
24466     
24467     // private
24468     initEditor : function(){
24469         //console.log("INIT EDITOR");
24470         this.assignDocWin();
24471         
24472         
24473         
24474         this.doc.designMode="on";
24475         this.doc.open();
24476         this.doc.write(this.getDocMarkup());
24477         this.doc.close();
24478         
24479         var dbody = (this.doc.body || this.doc.documentElement);
24480         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24481         // this copies styles from the containing element into thsi one..
24482         // not sure why we need all of this..
24483         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24484         
24485         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24486         //ss['background-attachment'] = 'fixed'; // w3c
24487         dbody.bgProperties = 'fixed'; // ie
24488         //Roo.DomHelper.applyStyles(dbody, ss);
24489         Roo.EventManager.on(this.doc, {
24490             //'mousedown': this.onEditorEvent,
24491             'mouseup': this.onEditorEvent,
24492             'dblclick': this.onEditorEvent,
24493             'click': this.onEditorEvent,
24494             'keyup': this.onEditorEvent,
24495             buffer:100,
24496             scope: this
24497         });
24498         if(Roo.isGecko){
24499             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24500         }
24501         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24502             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24503         }
24504         this.initialized = true;
24505
24506         this.owner.fireEvent('initialize', this);
24507         this.pushValue();
24508     },
24509
24510     // private
24511     onDestroy : function(){
24512         
24513         
24514         
24515         if(this.rendered){
24516             
24517             //for (var i =0; i < this.toolbars.length;i++) {
24518             //    // fixme - ask toolbars for heights?
24519             //    this.toolbars[i].onDestroy();
24520            // }
24521             
24522             //this.wrap.dom.innerHTML = '';
24523             //this.wrap.remove();
24524         }
24525     },
24526
24527     // private
24528     onFirstFocus : function(){
24529         
24530         this.assignDocWin();
24531         
24532         
24533         this.activated = true;
24534          
24535     
24536         if(Roo.isGecko){ // prevent silly gecko errors
24537             this.win.focus();
24538             var s = this.win.getSelection();
24539             if(!s.focusNode || s.focusNode.nodeType != 3){
24540                 var r = s.getRangeAt(0);
24541                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24542                 r.collapse(true);
24543                 this.deferFocus();
24544             }
24545             try{
24546                 this.execCmd('useCSS', true);
24547                 this.execCmd('styleWithCSS', false);
24548             }catch(e){}
24549         }
24550         this.owner.fireEvent('activate', this);
24551     },
24552
24553     // private
24554     adjustFont: function(btn){
24555         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24556         //if(Roo.isSafari){ // safari
24557         //    adjust *= 2;
24558        // }
24559         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24560         if(Roo.isSafari){ // safari
24561             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24562             v =  (v < 10) ? 10 : v;
24563             v =  (v > 48) ? 48 : v;
24564             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24565             
24566         }
24567         
24568         
24569         v = Math.max(1, v+adjust);
24570         
24571         this.execCmd('FontSize', v  );
24572     },
24573
24574     onEditorEvent : function(e)
24575     {
24576         this.owner.fireEvent('editorevent', this, e);
24577       //  this.updateToolbar();
24578         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24579     },
24580
24581     insertTag : function(tg)
24582     {
24583         // could be a bit smarter... -> wrap the current selected tRoo..
24584         if (tg.toLowerCase() == 'span' ||
24585             tg.toLowerCase() == 'code' ||
24586             tg.toLowerCase() == 'sup' ||
24587             tg.toLowerCase() == 'sub' 
24588             ) {
24589             
24590             range = this.createRange(this.getSelection());
24591             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24592             wrappingNode.appendChild(range.extractContents());
24593             range.insertNode(wrappingNode);
24594
24595             return;
24596             
24597             
24598             
24599         }
24600         this.execCmd("formatblock",   tg);
24601         
24602     },
24603     
24604     insertText : function(txt)
24605     {
24606         
24607         
24608         var range = this.createRange();
24609         range.deleteContents();
24610                //alert(Sender.getAttribute('label'));
24611                
24612         range.insertNode(this.doc.createTextNode(txt));
24613     } ,
24614     
24615      
24616
24617     /**
24618      * Executes a Midas editor command on the editor document and performs necessary focus and
24619      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24620      * @param {String} cmd The Midas command
24621      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24622      */
24623     relayCmd : function(cmd, value){
24624         this.win.focus();
24625         this.execCmd(cmd, value);
24626         this.owner.fireEvent('editorevent', this);
24627         //this.updateToolbar();
24628         this.owner.deferFocus();
24629     },
24630
24631     /**
24632      * Executes a Midas editor command directly on the editor document.
24633      * For visual commands, you should use {@link #relayCmd} instead.
24634      * <b>This should only be called after the editor is initialized.</b>
24635      * @param {String} cmd The Midas command
24636      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24637      */
24638     execCmd : function(cmd, value){
24639         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24640         this.syncValue();
24641     },
24642  
24643  
24644    
24645     /**
24646      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24647      * to insert tRoo.
24648      * @param {String} text | dom node.. 
24649      */
24650     insertAtCursor : function(text)
24651     {
24652         
24653         if(!this.activated){
24654             return;
24655         }
24656         /*
24657         if(Roo.isIE){
24658             this.win.focus();
24659             var r = this.doc.selection.createRange();
24660             if(r){
24661                 r.collapse(true);
24662                 r.pasteHTML(text);
24663                 this.syncValue();
24664                 this.deferFocus();
24665             
24666             }
24667             return;
24668         }
24669         */
24670         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24671             this.win.focus();
24672             
24673             
24674             // from jquery ui (MIT licenced)
24675             var range, node;
24676             var win = this.win;
24677             
24678             if (win.getSelection && win.getSelection().getRangeAt) {
24679                 range = win.getSelection().getRangeAt(0);
24680                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24681                 range.insertNode(node);
24682             } else if (win.document.selection && win.document.selection.createRange) {
24683                 // no firefox support
24684                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24685                 win.document.selection.createRange().pasteHTML(txt);
24686             } else {
24687                 // no firefox support
24688                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24689                 this.execCmd('InsertHTML', txt);
24690             } 
24691             
24692             this.syncValue();
24693             
24694             this.deferFocus();
24695         }
24696     },
24697  // private
24698     mozKeyPress : function(e){
24699         if(e.ctrlKey){
24700             var c = e.getCharCode(), cmd;
24701           
24702             if(c > 0){
24703                 c = String.fromCharCode(c).toLowerCase();
24704                 switch(c){
24705                     case 'b':
24706                         cmd = 'bold';
24707                         break;
24708                     case 'i':
24709                         cmd = 'italic';
24710                         break;
24711                     
24712                     case 'u':
24713                         cmd = 'underline';
24714                         break;
24715                     
24716                     case 'v':
24717                         this.cleanUpPaste.defer(100, this);
24718                         return;
24719                         
24720                 }
24721                 if(cmd){
24722                     this.win.focus();
24723                     this.execCmd(cmd);
24724                     this.deferFocus();
24725                     e.preventDefault();
24726                 }
24727                 
24728             }
24729         }
24730     },
24731
24732     // private
24733     fixKeys : function(){ // load time branching for fastest keydown performance
24734         if(Roo.isIE){
24735             return function(e){
24736                 var k = e.getKey(), r;
24737                 if(k == e.TAB){
24738                     e.stopEvent();
24739                     r = this.doc.selection.createRange();
24740                     if(r){
24741                         r.collapse(true);
24742                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24743                         this.deferFocus();
24744                     }
24745                     return;
24746                 }
24747                 
24748                 if(k == e.ENTER){
24749                     r = this.doc.selection.createRange();
24750                     if(r){
24751                         var target = r.parentElement();
24752                         if(!target || target.tagName.toLowerCase() != 'li'){
24753                             e.stopEvent();
24754                             r.pasteHTML('<br />');
24755                             r.collapse(false);
24756                             r.select();
24757                         }
24758                     }
24759                 }
24760                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24761                     this.cleanUpPaste.defer(100, this);
24762                     return;
24763                 }
24764                 
24765                 
24766             };
24767         }else if(Roo.isOpera){
24768             return function(e){
24769                 var k = e.getKey();
24770                 if(k == e.TAB){
24771                     e.stopEvent();
24772                     this.win.focus();
24773                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24774                     this.deferFocus();
24775                 }
24776                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24777                     this.cleanUpPaste.defer(100, this);
24778                     return;
24779                 }
24780                 
24781             };
24782         }else if(Roo.isSafari){
24783             return function(e){
24784                 var k = e.getKey();
24785                 
24786                 if(k == e.TAB){
24787                     e.stopEvent();
24788                     this.execCmd('InsertText','\t');
24789                     this.deferFocus();
24790                     return;
24791                 }
24792                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24793                     this.cleanUpPaste.defer(100, this);
24794                     return;
24795                 }
24796                 
24797              };
24798         }
24799     }(),
24800     
24801     getAllAncestors: function()
24802     {
24803         var p = this.getSelectedNode();
24804         var a = [];
24805         if (!p) {
24806             a.push(p); // push blank onto stack..
24807             p = this.getParentElement();
24808         }
24809         
24810         
24811         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24812             a.push(p);
24813             p = p.parentNode;
24814         }
24815         a.push(this.doc.body);
24816         return a;
24817     },
24818     lastSel : false,
24819     lastSelNode : false,
24820     
24821     
24822     getSelection : function() 
24823     {
24824         this.assignDocWin();
24825         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24826     },
24827     
24828     getSelectedNode: function() 
24829     {
24830         // this may only work on Gecko!!!
24831         
24832         // should we cache this!!!!
24833         
24834         
24835         
24836          
24837         var range = this.createRange(this.getSelection()).cloneRange();
24838         
24839         if (Roo.isIE) {
24840             var parent = range.parentElement();
24841             while (true) {
24842                 var testRange = range.duplicate();
24843                 testRange.moveToElementText(parent);
24844                 if (testRange.inRange(range)) {
24845                     break;
24846                 }
24847                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24848                     break;
24849                 }
24850                 parent = parent.parentElement;
24851             }
24852             return parent;
24853         }
24854         
24855         // is ancestor a text element.
24856         var ac =  range.commonAncestorContainer;
24857         if (ac.nodeType == 3) {
24858             ac = ac.parentNode;
24859         }
24860         
24861         var ar = ac.childNodes;
24862          
24863         var nodes = [];
24864         var other_nodes = [];
24865         var has_other_nodes = false;
24866         for (var i=0;i<ar.length;i++) {
24867             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24868                 continue;
24869             }
24870             // fullly contained node.
24871             
24872             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24873                 nodes.push(ar[i]);
24874                 continue;
24875             }
24876             
24877             // probably selected..
24878             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24879                 other_nodes.push(ar[i]);
24880                 continue;
24881             }
24882             // outer..
24883             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24884                 continue;
24885             }
24886             
24887             
24888             has_other_nodes = true;
24889         }
24890         if (!nodes.length && other_nodes.length) {
24891             nodes= other_nodes;
24892         }
24893         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24894             return false;
24895         }
24896         
24897         return nodes[0];
24898     },
24899     createRange: function(sel)
24900     {
24901         // this has strange effects when using with 
24902         // top toolbar - not sure if it's a great idea.
24903         //this.editor.contentWindow.focus();
24904         if (typeof sel != "undefined") {
24905             try {
24906                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24907             } catch(e) {
24908                 return this.doc.createRange();
24909             }
24910         } else {
24911             return this.doc.createRange();
24912         }
24913     },
24914     getParentElement: function()
24915     {
24916         
24917         this.assignDocWin();
24918         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24919         
24920         var range = this.createRange(sel);
24921          
24922         try {
24923             var p = range.commonAncestorContainer;
24924             while (p.nodeType == 3) { // text node
24925                 p = p.parentNode;
24926             }
24927             return p;
24928         } catch (e) {
24929             return null;
24930         }
24931     
24932     },
24933     /***
24934      *
24935      * Range intersection.. the hard stuff...
24936      *  '-1' = before
24937      *  '0' = hits..
24938      *  '1' = after.
24939      *         [ -- selected range --- ]
24940      *   [fail]                        [fail]
24941      *
24942      *    basically..
24943      *      if end is before start or  hits it. fail.
24944      *      if start is after end or hits it fail.
24945      *
24946      *   if either hits (but other is outside. - then it's not 
24947      *   
24948      *    
24949      **/
24950     
24951     
24952     // @see http://www.thismuchiknow.co.uk/?p=64.
24953     rangeIntersectsNode : function(range, node)
24954     {
24955         var nodeRange = node.ownerDocument.createRange();
24956         try {
24957             nodeRange.selectNode(node);
24958         } catch (e) {
24959             nodeRange.selectNodeContents(node);
24960         }
24961     
24962         var rangeStartRange = range.cloneRange();
24963         rangeStartRange.collapse(true);
24964     
24965         var rangeEndRange = range.cloneRange();
24966         rangeEndRange.collapse(false);
24967     
24968         var nodeStartRange = nodeRange.cloneRange();
24969         nodeStartRange.collapse(true);
24970     
24971         var nodeEndRange = nodeRange.cloneRange();
24972         nodeEndRange.collapse(false);
24973     
24974         return rangeStartRange.compareBoundaryPoints(
24975                  Range.START_TO_START, nodeEndRange) == -1 &&
24976                rangeEndRange.compareBoundaryPoints(
24977                  Range.START_TO_START, nodeStartRange) == 1;
24978         
24979          
24980     },
24981     rangeCompareNode : function(range, node)
24982     {
24983         var nodeRange = node.ownerDocument.createRange();
24984         try {
24985             nodeRange.selectNode(node);
24986         } catch (e) {
24987             nodeRange.selectNodeContents(node);
24988         }
24989         
24990         
24991         range.collapse(true);
24992     
24993         nodeRange.collapse(true);
24994      
24995         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24996         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24997          
24998         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24999         
25000         var nodeIsBefore   =  ss == 1;
25001         var nodeIsAfter    = ee == -1;
25002         
25003         if (nodeIsBefore && nodeIsAfter) {
25004             return 0; // outer
25005         }
25006         if (!nodeIsBefore && nodeIsAfter) {
25007             return 1; //right trailed.
25008         }
25009         
25010         if (nodeIsBefore && !nodeIsAfter) {
25011             return 2;  // left trailed.
25012         }
25013         // fully contined.
25014         return 3;
25015     },
25016
25017     // private? - in a new class?
25018     cleanUpPaste :  function()
25019     {
25020         // cleans up the whole document..
25021         Roo.log('cleanuppaste');
25022         
25023         this.cleanUpChildren(this.doc.body);
25024         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25025         if (clean != this.doc.body.innerHTML) {
25026             this.doc.body.innerHTML = clean;
25027         }
25028         
25029     },
25030     
25031     cleanWordChars : function(input) {// change the chars to hex code
25032         var he = Roo.HtmlEditorCore;
25033         
25034         var output = input;
25035         Roo.each(he.swapCodes, function(sw) { 
25036             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25037             
25038             output = output.replace(swapper, sw[1]);
25039         });
25040         
25041         return output;
25042     },
25043     
25044     
25045     cleanUpChildren : function (n)
25046     {
25047         if (!n.childNodes.length) {
25048             return;
25049         }
25050         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25051            this.cleanUpChild(n.childNodes[i]);
25052         }
25053     },
25054     
25055     
25056         
25057     
25058     cleanUpChild : function (node)
25059     {
25060         var ed = this;
25061         //console.log(node);
25062         if (node.nodeName == "#text") {
25063             // clean up silly Windows -- stuff?
25064             return; 
25065         }
25066         if (node.nodeName == "#comment") {
25067             node.parentNode.removeChild(node);
25068             // clean up silly Windows -- stuff?
25069             return; 
25070         }
25071         var lcname = node.tagName.toLowerCase();
25072         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25073         // whitelist of tags..
25074         
25075         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25076             // remove node.
25077             node.parentNode.removeChild(node);
25078             return;
25079             
25080         }
25081         
25082         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25083         
25084         // spans with no attributes - just remove them..
25085         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25086             remove_keep_children = true;
25087         }
25088         
25089         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25090         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25091         
25092         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25093         //    remove_keep_children = true;
25094         //}
25095         
25096         if (remove_keep_children) {
25097             this.cleanUpChildren(node);
25098             // inserts everything just before this node...
25099             while (node.childNodes.length) {
25100                 var cn = node.childNodes[0];
25101                 node.removeChild(cn);
25102                 node.parentNode.insertBefore(cn, node);
25103             }
25104             node.parentNode.removeChild(node);
25105             return;
25106         }
25107         
25108         if (!node.attributes || !node.attributes.length) {
25109             
25110           
25111             
25112             
25113             this.cleanUpChildren(node);
25114             return;
25115         }
25116         
25117         function cleanAttr(n,v)
25118         {
25119             
25120             if (v.match(/^\./) || v.match(/^\//)) {
25121                 return;
25122             }
25123             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25124                 return;
25125             }
25126             if (v.match(/^#/)) {
25127                 return;
25128             }
25129             if (v.match(/^\{/)) { // allow template editing.
25130                 return;
25131             }
25132 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25133             node.removeAttribute(n);
25134             
25135         }
25136         
25137         var cwhite = this.cwhite;
25138         var cblack = this.cblack;
25139             
25140         function cleanStyle(n,v)
25141         {
25142             if (v.match(/expression/)) { //XSS?? should we even bother..
25143                 node.removeAttribute(n);
25144                 return;
25145             }
25146             
25147             var parts = v.split(/;/);
25148             var clean = [];
25149             
25150             Roo.each(parts, function(p) {
25151                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25152                 if (!p.length) {
25153                     return true;
25154                 }
25155                 var l = p.split(':').shift().replace(/\s+/g,'');
25156                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25157                 
25158                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25159 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25160                     //node.removeAttribute(n);
25161                     return true;
25162                 }
25163                 //Roo.log()
25164                 // only allow 'c whitelisted system attributes'
25165                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25166 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25167                     //node.removeAttribute(n);
25168                     return true;
25169                 }
25170                 
25171                 
25172                  
25173                 
25174                 clean.push(p);
25175                 return true;
25176             });
25177             if (clean.length) { 
25178                 node.setAttribute(n, clean.join(';'));
25179             } else {
25180                 node.removeAttribute(n);
25181             }
25182             
25183         }
25184         
25185         
25186         for (var i = node.attributes.length-1; i > -1 ; i--) {
25187             var a = node.attributes[i];
25188             //console.log(a);
25189             
25190             if (a.name.toLowerCase().substr(0,2)=='on')  {
25191                 node.removeAttribute(a.name);
25192                 continue;
25193             }
25194             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25195                 node.removeAttribute(a.name);
25196                 continue;
25197             }
25198             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25199                 cleanAttr(a.name,a.value); // fixme..
25200                 continue;
25201             }
25202             if (a.name == 'style') {
25203                 cleanStyle(a.name,a.value);
25204                 continue;
25205             }
25206             /// clean up MS crap..
25207             // tecnically this should be a list of valid class'es..
25208             
25209             
25210             if (a.name == 'class') {
25211                 if (a.value.match(/^Mso/)) {
25212                     node.removeAttribute('class');
25213                 }
25214                 
25215                 if (a.value.match(/^body$/)) {
25216                     node.removeAttribute('class');
25217                 }
25218                 continue;
25219             }
25220             
25221             // style cleanup!?
25222             // class cleanup?
25223             
25224         }
25225         
25226         
25227         this.cleanUpChildren(node);
25228         
25229         
25230     },
25231     
25232     /**
25233      * Clean up MS wordisms...
25234      */
25235     cleanWord : function(node)
25236     {
25237         if (!node) {
25238             this.cleanWord(this.doc.body);
25239             return;
25240         }
25241         
25242         if(
25243                 node.nodeName == 'SPAN' &&
25244                 !node.hasAttributes() &&
25245                 node.childNodes.length == 1 &&
25246                 node.firstChild.nodeName == "#text"  
25247         ) {
25248             var textNode = node.firstChild;
25249             node.removeChild(textNode);
25250             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25251                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25252             }
25253             node.parentNode.insertBefore(textNode, node);
25254             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25255                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25256             }
25257             node.parentNode.removeChild(node);
25258         }
25259         
25260         if (node.nodeName == "#text") {
25261             // clean up silly Windows -- stuff?
25262             return; 
25263         }
25264         if (node.nodeName == "#comment") {
25265             node.parentNode.removeChild(node);
25266             // clean up silly Windows -- stuff?
25267             return; 
25268         }
25269         
25270         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25271             node.parentNode.removeChild(node);
25272             return;
25273         }
25274         //Roo.log(node.tagName);
25275         // remove - but keep children..
25276         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25277             //Roo.log('-- removed');
25278             while (node.childNodes.length) {
25279                 var cn = node.childNodes[0];
25280                 node.removeChild(cn);
25281                 node.parentNode.insertBefore(cn, node);
25282                 // move node to parent - and clean it..
25283                 this.cleanWord(cn);
25284             }
25285             node.parentNode.removeChild(node);
25286             /// no need to iterate chidlren = it's got none..
25287             //this.iterateChildren(node, this.cleanWord);
25288             return;
25289         }
25290         // clean styles
25291         if (node.className.length) {
25292             
25293             var cn = node.className.split(/\W+/);
25294             var cna = [];
25295             Roo.each(cn, function(cls) {
25296                 if (cls.match(/Mso[a-zA-Z]+/)) {
25297                     return;
25298                 }
25299                 cna.push(cls);
25300             });
25301             node.className = cna.length ? cna.join(' ') : '';
25302             if (!cna.length) {
25303                 node.removeAttribute("class");
25304             }
25305         }
25306         
25307         if (node.hasAttribute("lang")) {
25308             node.removeAttribute("lang");
25309         }
25310         
25311         if (node.hasAttribute("style")) {
25312             
25313             var styles = node.getAttribute("style").split(";");
25314             var nstyle = [];
25315             Roo.each(styles, function(s) {
25316                 if (!s.match(/:/)) {
25317                     return;
25318                 }
25319                 var kv = s.split(":");
25320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25321                     return;
25322                 }
25323                 // what ever is left... we allow.
25324                 nstyle.push(s);
25325             });
25326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25327             if (!nstyle.length) {
25328                 node.removeAttribute('style');
25329             }
25330         }
25331         this.iterateChildren(node, this.cleanWord);
25332         
25333         
25334         
25335     },
25336     /**
25337      * iterateChildren of a Node, calling fn each time, using this as the scole..
25338      * @param {DomNode} node node to iterate children of.
25339      * @param {Function} fn method of this class to call on each item.
25340      */
25341     iterateChildren : function(node, fn)
25342     {
25343         if (!node.childNodes.length) {
25344                 return;
25345         }
25346         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25347            fn.call(this, node.childNodes[i])
25348         }
25349     },
25350     
25351     
25352     /**
25353      * cleanTableWidths.
25354      *
25355      * Quite often pasting from word etc.. results in tables with column and widths.
25356      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25357      *
25358      */
25359     cleanTableWidths : function(node)
25360     {
25361          
25362          
25363         if (!node) {
25364             this.cleanTableWidths(this.doc.body);
25365             return;
25366         }
25367         
25368         // ignore list...
25369         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25370             return; 
25371         }
25372         Roo.log(node.tagName);
25373         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25374             this.iterateChildren(node, this.cleanTableWidths);
25375             return;
25376         }
25377         if (node.hasAttribute('width')) {
25378             node.removeAttribute('width');
25379         }
25380         
25381          
25382         if (node.hasAttribute("style")) {
25383             // pretty basic...
25384             
25385             var styles = node.getAttribute("style").split(";");
25386             var nstyle = [];
25387             Roo.each(styles, function(s) {
25388                 if (!s.match(/:/)) {
25389                     return;
25390                 }
25391                 var kv = s.split(":");
25392                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25393                     return;
25394                 }
25395                 // what ever is left... we allow.
25396                 nstyle.push(s);
25397             });
25398             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25399             if (!nstyle.length) {
25400                 node.removeAttribute('style');
25401             }
25402         }
25403         
25404         this.iterateChildren(node, this.cleanTableWidths);
25405         
25406         
25407     },
25408     
25409     
25410     
25411     
25412     domToHTML : function(currentElement, depth, nopadtext) {
25413         
25414         depth = depth || 0;
25415         nopadtext = nopadtext || false;
25416     
25417         if (!currentElement) {
25418             return this.domToHTML(this.doc.body);
25419         }
25420         
25421         //Roo.log(currentElement);
25422         var j;
25423         var allText = false;
25424         var nodeName = currentElement.nodeName;
25425         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25426         
25427         if  (nodeName == '#text') {
25428             
25429             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25430         }
25431         
25432         
25433         var ret = '';
25434         if (nodeName != 'BODY') {
25435              
25436             var i = 0;
25437             // Prints the node tagName, such as <A>, <IMG>, etc
25438             if (tagName) {
25439                 var attr = [];
25440                 for(i = 0; i < currentElement.attributes.length;i++) {
25441                     // quoting?
25442                     var aname = currentElement.attributes.item(i).name;
25443                     if (!currentElement.attributes.item(i).value.length) {
25444                         continue;
25445                     }
25446                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25447                 }
25448                 
25449                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25450             } 
25451             else {
25452                 
25453                 // eack
25454             }
25455         } else {
25456             tagName = false;
25457         }
25458         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25459             return ret;
25460         }
25461         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25462             nopadtext = true;
25463         }
25464         
25465         
25466         // Traverse the tree
25467         i = 0;
25468         var currentElementChild = currentElement.childNodes.item(i);
25469         var allText = true;
25470         var innerHTML  = '';
25471         lastnode = '';
25472         while (currentElementChild) {
25473             // Formatting code (indent the tree so it looks nice on the screen)
25474             var nopad = nopadtext;
25475             if (lastnode == 'SPAN') {
25476                 nopad  = true;
25477             }
25478             // text
25479             if  (currentElementChild.nodeName == '#text') {
25480                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25481                 toadd = nopadtext ? toadd : toadd.trim();
25482                 if (!nopad && toadd.length > 80) {
25483                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25484                 }
25485                 innerHTML  += toadd;
25486                 
25487                 i++;
25488                 currentElementChild = currentElement.childNodes.item(i);
25489                 lastNode = '';
25490                 continue;
25491             }
25492             allText = false;
25493             
25494             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25495                 
25496             // Recursively traverse the tree structure of the child node
25497             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25498             lastnode = currentElementChild.nodeName;
25499             i++;
25500             currentElementChild=currentElement.childNodes.item(i);
25501         }
25502         
25503         ret += innerHTML;
25504         
25505         if (!allText) {
25506                 // The remaining code is mostly for formatting the tree
25507             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25508         }
25509         
25510         
25511         if (tagName) {
25512             ret+= "</"+tagName+">";
25513         }
25514         return ret;
25515         
25516     },
25517         
25518     applyBlacklists : function()
25519     {
25520         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25521         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25522         
25523         this.white = [];
25524         this.black = [];
25525         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25526             if (b.indexOf(tag) > -1) {
25527                 return;
25528             }
25529             this.white.push(tag);
25530             
25531         }, this);
25532         
25533         Roo.each(w, function(tag) {
25534             if (b.indexOf(tag) > -1) {
25535                 return;
25536             }
25537             if (this.white.indexOf(tag) > -1) {
25538                 return;
25539             }
25540             this.white.push(tag);
25541             
25542         }, this);
25543         
25544         
25545         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25546             if (w.indexOf(tag) > -1) {
25547                 return;
25548             }
25549             this.black.push(tag);
25550             
25551         }, this);
25552         
25553         Roo.each(b, function(tag) {
25554             if (w.indexOf(tag) > -1) {
25555                 return;
25556             }
25557             if (this.black.indexOf(tag) > -1) {
25558                 return;
25559             }
25560             this.black.push(tag);
25561             
25562         }, this);
25563         
25564         
25565         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25566         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25567         
25568         this.cwhite = [];
25569         this.cblack = [];
25570         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25571             if (b.indexOf(tag) > -1) {
25572                 return;
25573             }
25574             this.cwhite.push(tag);
25575             
25576         }, this);
25577         
25578         Roo.each(w, function(tag) {
25579             if (b.indexOf(tag) > -1) {
25580                 return;
25581             }
25582             if (this.cwhite.indexOf(tag) > -1) {
25583                 return;
25584             }
25585             this.cwhite.push(tag);
25586             
25587         }, this);
25588         
25589         
25590         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25591             if (w.indexOf(tag) > -1) {
25592                 return;
25593             }
25594             this.cblack.push(tag);
25595             
25596         }, this);
25597         
25598         Roo.each(b, function(tag) {
25599             if (w.indexOf(tag) > -1) {
25600                 return;
25601             }
25602             if (this.cblack.indexOf(tag) > -1) {
25603                 return;
25604             }
25605             this.cblack.push(tag);
25606             
25607         }, this);
25608     },
25609     
25610     setStylesheets : function(stylesheets)
25611     {
25612         if(typeof(stylesheets) == 'string'){
25613             Roo.get(this.iframe.contentDocument.head).createChild({
25614                 tag : 'link',
25615                 rel : 'stylesheet',
25616                 type : 'text/css',
25617                 href : stylesheets
25618             });
25619             
25620             return;
25621         }
25622         var _this = this;
25623      
25624         Roo.each(stylesheets, function(s) {
25625             if(!s.length){
25626                 return;
25627             }
25628             
25629             Roo.get(_this.iframe.contentDocument.head).createChild({
25630                 tag : 'link',
25631                 rel : 'stylesheet',
25632                 type : 'text/css',
25633                 href : s
25634             });
25635         });
25636
25637         
25638     },
25639     
25640     removeStylesheets : function()
25641     {
25642         var _this = this;
25643         
25644         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25645             s.remove();
25646         });
25647     },
25648     
25649     setStyle : function(style)
25650     {
25651         Roo.get(this.iframe.contentDocument.head).createChild({
25652             tag : 'style',
25653             type : 'text/css',
25654             html : style
25655         });
25656
25657         return;
25658     }
25659     
25660     // hide stuff that is not compatible
25661     /**
25662      * @event blur
25663      * @hide
25664      */
25665     /**
25666      * @event change
25667      * @hide
25668      */
25669     /**
25670      * @event focus
25671      * @hide
25672      */
25673     /**
25674      * @event specialkey
25675      * @hide
25676      */
25677     /**
25678      * @cfg {String} fieldClass @hide
25679      */
25680     /**
25681      * @cfg {String} focusClass @hide
25682      */
25683     /**
25684      * @cfg {String} autoCreate @hide
25685      */
25686     /**
25687      * @cfg {String} inputType @hide
25688      */
25689     /**
25690      * @cfg {String} invalidClass @hide
25691      */
25692     /**
25693      * @cfg {String} invalidText @hide
25694      */
25695     /**
25696      * @cfg {String} msgFx @hide
25697      */
25698     /**
25699      * @cfg {String} validateOnBlur @hide
25700      */
25701 });
25702
25703 Roo.HtmlEditorCore.white = [
25704         'area', 'br', 'img', 'input', 'hr', 'wbr',
25705         
25706        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25707        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25708        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25709        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25710        'table',   'ul',         'xmp', 
25711        
25712        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25713       'thead',   'tr', 
25714      
25715       'dir', 'menu', 'ol', 'ul', 'dl',
25716        
25717       'embed',  'object'
25718 ];
25719
25720
25721 Roo.HtmlEditorCore.black = [
25722     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25723         'applet', // 
25724         'base',   'basefont', 'bgsound', 'blink',  'body', 
25725         'frame',  'frameset', 'head',    'html',   'ilayer', 
25726         'iframe', 'layer',  'link',     'meta',    'object',   
25727         'script', 'style' ,'title',  'xml' // clean later..
25728 ];
25729 Roo.HtmlEditorCore.clean = [
25730     'script', 'style', 'title', 'xml'
25731 ];
25732 Roo.HtmlEditorCore.remove = [
25733     'font'
25734 ];
25735 // attributes..
25736
25737 Roo.HtmlEditorCore.ablack = [
25738     'on'
25739 ];
25740     
25741 Roo.HtmlEditorCore.aclean = [ 
25742     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25743 ];
25744
25745 // protocols..
25746 Roo.HtmlEditorCore.pwhite= [
25747         'http',  'https',  'mailto'
25748 ];
25749
25750 // white listed style attributes.
25751 Roo.HtmlEditorCore.cwhite= [
25752       //  'text-align', /// default is to allow most things..
25753       
25754          
25755 //        'font-size'//??
25756 ];
25757
25758 // black listed style attributes.
25759 Roo.HtmlEditorCore.cblack= [
25760       //  'font-size' -- this can be set by the project 
25761 ];
25762
25763
25764 Roo.HtmlEditorCore.swapCodes   =[ 
25765     [    8211, "--" ], 
25766     [    8212, "--" ], 
25767     [    8216,  "'" ],  
25768     [    8217, "'" ],  
25769     [    8220, '"' ],  
25770     [    8221, '"' ],  
25771     [    8226, "*" ],  
25772     [    8230, "..." ]
25773 ]; 
25774
25775     /*
25776  * - LGPL
25777  *
25778  * HtmlEditor
25779  * 
25780  */
25781
25782 /**
25783  * @class Roo.bootstrap.HtmlEditor
25784  * @extends Roo.bootstrap.TextArea
25785  * Bootstrap HtmlEditor class
25786
25787  * @constructor
25788  * Create a new HtmlEditor
25789  * @param {Object} config The config object
25790  */
25791
25792 Roo.bootstrap.HtmlEditor = function(config){
25793     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25794     if (!this.toolbars) {
25795         this.toolbars = [];
25796     }
25797     
25798     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25799     this.addEvents({
25800             /**
25801              * @event initialize
25802              * Fires when the editor is fully initialized (including the iframe)
25803              * @param {HtmlEditor} this
25804              */
25805             initialize: true,
25806             /**
25807              * @event activate
25808              * Fires when the editor is first receives the focus. Any insertion must wait
25809              * until after this event.
25810              * @param {HtmlEditor} this
25811              */
25812             activate: true,
25813              /**
25814              * @event beforesync
25815              * Fires before the textarea is updated with content from the editor iframe. Return false
25816              * to cancel the sync.
25817              * @param {HtmlEditor} this
25818              * @param {String} html
25819              */
25820             beforesync: true,
25821              /**
25822              * @event beforepush
25823              * Fires before the iframe editor is updated with content from the textarea. Return false
25824              * to cancel the push.
25825              * @param {HtmlEditor} this
25826              * @param {String} html
25827              */
25828             beforepush: true,
25829              /**
25830              * @event sync
25831              * Fires when the textarea is updated with content from the editor iframe.
25832              * @param {HtmlEditor} this
25833              * @param {String} html
25834              */
25835             sync: true,
25836              /**
25837              * @event push
25838              * Fires when the iframe editor is updated with content from the textarea.
25839              * @param {HtmlEditor} this
25840              * @param {String} html
25841              */
25842             push: true,
25843              /**
25844              * @event editmodechange
25845              * Fires when the editor switches edit modes
25846              * @param {HtmlEditor} this
25847              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25848              */
25849             editmodechange: true,
25850             /**
25851              * @event editorevent
25852              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25853              * @param {HtmlEditor} this
25854              */
25855             editorevent: true,
25856             /**
25857              * @event firstfocus
25858              * Fires when on first focus - needed by toolbars..
25859              * @param {HtmlEditor} this
25860              */
25861             firstfocus: true,
25862             /**
25863              * @event autosave
25864              * Auto save the htmlEditor value as a file into Events
25865              * @param {HtmlEditor} this
25866              */
25867             autosave: true,
25868             /**
25869              * @event savedpreview
25870              * preview the saved version of htmlEditor
25871              * @param {HtmlEditor} this
25872              */
25873             savedpreview: true
25874         });
25875 };
25876
25877
25878 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25879     
25880     
25881       /**
25882      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25883      */
25884     toolbars : false,
25885     
25886      /**
25887     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25888     */
25889     btns : [],
25890    
25891      /**
25892      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25893      *                        Roo.resizable.
25894      */
25895     resizable : false,
25896      /**
25897      * @cfg {Number} height (in pixels)
25898      */   
25899     height: 300,
25900    /**
25901      * @cfg {Number} width (in pixels)
25902      */   
25903     width: false,
25904     
25905     /**
25906      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25907      * 
25908      */
25909     stylesheets: false,
25910     
25911     // id of frame..
25912     frameId: false,
25913     
25914     // private properties
25915     validationEvent : false,
25916     deferHeight: true,
25917     initialized : false,
25918     activated : false,
25919     
25920     onFocus : Roo.emptyFn,
25921     iframePad:3,
25922     hideMode:'offsets',
25923     
25924     tbContainer : false,
25925     
25926     bodyCls : '',
25927     
25928     toolbarContainer :function() {
25929         return this.wrap.select('.x-html-editor-tb',true).first();
25930     },
25931
25932     /**
25933      * Protected method that will not generally be called directly. It
25934      * is called when the editor creates its toolbar. Override this method if you need to
25935      * add custom toolbar buttons.
25936      * @param {HtmlEditor} editor
25937      */
25938     createToolbar : function(){
25939         Roo.log('renewing');
25940         Roo.log("create toolbars");
25941         
25942         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25943         this.toolbars[0].render(this.toolbarContainer());
25944         
25945         return;
25946         
25947 //        if (!editor.toolbars || !editor.toolbars.length) {
25948 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25949 //        }
25950 //        
25951 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25952 //            editor.toolbars[i] = Roo.factory(
25953 //                    typeof(editor.toolbars[i]) == 'string' ?
25954 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25955 //                Roo.bootstrap.HtmlEditor);
25956 //            editor.toolbars[i].init(editor);
25957 //        }
25958     },
25959
25960      
25961     // private
25962     onRender : function(ct, position)
25963     {
25964        // Roo.log("Call onRender: " + this.xtype);
25965         var _t = this;
25966         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25967       
25968         this.wrap = this.inputEl().wrap({
25969             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25970         });
25971         
25972         this.editorcore.onRender(ct, position);
25973          
25974         if (this.resizable) {
25975             this.resizeEl = new Roo.Resizable(this.wrap, {
25976                 pinned : true,
25977                 wrap: true,
25978                 dynamic : true,
25979                 minHeight : this.height,
25980                 height: this.height,
25981                 handles : this.resizable,
25982                 width: this.width,
25983                 listeners : {
25984                     resize : function(r, w, h) {
25985                         _t.onResize(w,h); // -something
25986                     }
25987                 }
25988             });
25989             
25990         }
25991         this.createToolbar(this);
25992        
25993         
25994         if(!this.width && this.resizable){
25995             this.setSize(this.wrap.getSize());
25996         }
25997         if (this.resizeEl) {
25998             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25999             // should trigger onReize..
26000         }
26001         
26002     },
26003
26004     // private
26005     onResize : function(w, h)
26006     {
26007         Roo.log('resize: ' +w + ',' + h );
26008         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26009         var ew = false;
26010         var eh = false;
26011         
26012         if(this.inputEl() ){
26013             if(typeof w == 'number'){
26014                 var aw = w - this.wrap.getFrameWidth('lr');
26015                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26016                 ew = aw;
26017             }
26018             if(typeof h == 'number'){
26019                  var tbh = -11;  // fixme it needs to tool bar size!
26020                 for (var i =0; i < this.toolbars.length;i++) {
26021                     // fixme - ask toolbars for heights?
26022                     tbh += this.toolbars[i].el.getHeight();
26023                     //if (this.toolbars[i].footer) {
26024                     //    tbh += this.toolbars[i].footer.el.getHeight();
26025                     //}
26026                 }
26027               
26028                 
26029                 
26030                 
26031                 
26032                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26033                 ah -= 5; // knock a few pixes off for look..
26034                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26035                 var eh = ah;
26036             }
26037         }
26038         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26039         this.editorcore.onResize(ew,eh);
26040         
26041     },
26042
26043     /**
26044      * Toggles the editor between standard and source edit mode.
26045      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26046      */
26047     toggleSourceEdit : function(sourceEditMode)
26048     {
26049         this.editorcore.toggleSourceEdit(sourceEditMode);
26050         
26051         if(this.editorcore.sourceEditMode){
26052             Roo.log('editor - showing textarea');
26053             
26054 //            Roo.log('in');
26055 //            Roo.log(this.syncValue());
26056             this.syncValue();
26057             this.inputEl().removeClass(['hide', 'x-hidden']);
26058             this.inputEl().dom.removeAttribute('tabIndex');
26059             this.inputEl().focus();
26060         }else{
26061             Roo.log('editor - hiding textarea');
26062 //            Roo.log('out')
26063 //            Roo.log(this.pushValue()); 
26064             this.pushValue();
26065             
26066             this.inputEl().addClass(['hide', 'x-hidden']);
26067             this.inputEl().dom.setAttribute('tabIndex', -1);
26068             //this.deferFocus();
26069         }
26070          
26071         if(this.resizable){
26072             this.setSize(this.wrap.getSize());
26073         }
26074         
26075         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26076     },
26077  
26078     // private (for BoxComponent)
26079     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26080
26081     // private (for BoxComponent)
26082     getResizeEl : function(){
26083         return this.wrap;
26084     },
26085
26086     // private (for BoxComponent)
26087     getPositionEl : function(){
26088         return this.wrap;
26089     },
26090
26091     // private
26092     initEvents : function(){
26093         this.originalValue = this.getValue();
26094     },
26095
26096 //    /**
26097 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26098 //     * @method
26099 //     */
26100 //    markInvalid : Roo.emptyFn,
26101 //    /**
26102 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26103 //     * @method
26104 //     */
26105 //    clearInvalid : Roo.emptyFn,
26106
26107     setValue : function(v){
26108         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26109         this.editorcore.pushValue();
26110     },
26111
26112      
26113     // private
26114     deferFocus : function(){
26115         this.focus.defer(10, this);
26116     },
26117
26118     // doc'ed in Field
26119     focus : function(){
26120         this.editorcore.focus();
26121         
26122     },
26123       
26124
26125     // private
26126     onDestroy : function(){
26127         
26128         
26129         
26130         if(this.rendered){
26131             
26132             for (var i =0; i < this.toolbars.length;i++) {
26133                 // fixme - ask toolbars for heights?
26134                 this.toolbars[i].onDestroy();
26135             }
26136             
26137             this.wrap.dom.innerHTML = '';
26138             this.wrap.remove();
26139         }
26140     },
26141
26142     // private
26143     onFirstFocus : function(){
26144         //Roo.log("onFirstFocus");
26145         this.editorcore.onFirstFocus();
26146          for (var i =0; i < this.toolbars.length;i++) {
26147             this.toolbars[i].onFirstFocus();
26148         }
26149         
26150     },
26151     
26152     // private
26153     syncValue : function()
26154     {   
26155         this.editorcore.syncValue();
26156     },
26157     
26158     pushValue : function()
26159     {   
26160         this.editorcore.pushValue();
26161     }
26162      
26163     
26164     // hide stuff that is not compatible
26165     /**
26166      * @event blur
26167      * @hide
26168      */
26169     /**
26170      * @event change
26171      * @hide
26172      */
26173     /**
26174      * @event focus
26175      * @hide
26176      */
26177     /**
26178      * @event specialkey
26179      * @hide
26180      */
26181     /**
26182      * @cfg {String} fieldClass @hide
26183      */
26184     /**
26185      * @cfg {String} focusClass @hide
26186      */
26187     /**
26188      * @cfg {String} autoCreate @hide
26189      */
26190     /**
26191      * @cfg {String} inputType @hide
26192      */
26193      
26194     /**
26195      * @cfg {String} invalidText @hide
26196      */
26197     /**
26198      * @cfg {String} msgFx @hide
26199      */
26200     /**
26201      * @cfg {String} validateOnBlur @hide
26202      */
26203 });
26204  
26205     
26206    
26207    
26208    
26209       
26210 Roo.namespace('Roo.bootstrap.htmleditor');
26211 /**
26212  * @class Roo.bootstrap.HtmlEditorToolbar1
26213  * Basic Toolbar
26214  * 
26215  * @example
26216  * Usage:
26217  *
26218  new Roo.bootstrap.HtmlEditor({
26219     ....
26220     toolbars : [
26221         new Roo.bootstrap.HtmlEditorToolbar1({
26222             disable : { fonts: 1 , format: 1, ..., ... , ...],
26223             btns : [ .... ]
26224         })
26225     }
26226      
26227  * 
26228  * @cfg {Object} disable List of elements to disable..
26229  * @cfg {Array} btns List of additional buttons.
26230  * 
26231  * 
26232  * NEEDS Extra CSS? 
26233  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26234  */
26235  
26236 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26237 {
26238     
26239     Roo.apply(this, config);
26240     
26241     // default disabled, based on 'good practice'..
26242     this.disable = this.disable || {};
26243     Roo.applyIf(this.disable, {
26244         fontSize : true,
26245         colors : true,
26246         specialElements : true
26247     });
26248     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26249     
26250     this.editor = config.editor;
26251     this.editorcore = config.editor.editorcore;
26252     
26253     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26254     
26255     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26256     // dont call parent... till later.
26257 }
26258 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26259      
26260     bar : true,
26261     
26262     editor : false,
26263     editorcore : false,
26264     
26265     
26266     formats : [
26267         "p" ,  
26268         "h1","h2","h3","h4","h5","h6", 
26269         "pre", "code", 
26270         "abbr", "acronym", "address", "cite", "samp", "var",
26271         'div','span'
26272     ],
26273     
26274     onRender : function(ct, position)
26275     {
26276        // Roo.log("Call onRender: " + this.xtype);
26277         
26278        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26279        Roo.log(this.el);
26280        this.el.dom.style.marginBottom = '0';
26281        var _this = this;
26282        var editorcore = this.editorcore;
26283        var editor= this.editor;
26284        
26285        var children = [];
26286        var btn = function(id,cmd , toggle, handler, html){
26287        
26288             var  event = toggle ? 'toggle' : 'click';
26289        
26290             var a = {
26291                 size : 'sm',
26292                 xtype: 'Button',
26293                 xns: Roo.bootstrap,
26294                 //glyphicon : id,
26295                 fa: id,
26296                 cmd : id || cmd,
26297                 enableToggle:toggle !== false,
26298                 html : html || '',
26299                 pressed : toggle ? false : null,
26300                 listeners : {}
26301             };
26302             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26303                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26304             };
26305             children.push(a);
26306             return a;
26307        }
26308        
26309     //    var cb_box = function...
26310         
26311         var style = {
26312                 xtype: 'Button',
26313                 size : 'sm',
26314                 xns: Roo.bootstrap,
26315                 fa : 'font',
26316                 //html : 'submit'
26317                 menu : {
26318                     xtype: 'Menu',
26319                     xns: Roo.bootstrap,
26320                     items:  []
26321                 }
26322         };
26323         Roo.each(this.formats, function(f) {
26324             style.menu.items.push({
26325                 xtype :'MenuItem',
26326                 xns: Roo.bootstrap,
26327                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26328                 tagname : f,
26329                 listeners : {
26330                     click : function()
26331                     {
26332                         editorcore.insertTag(this.tagname);
26333                         editor.focus();
26334                     }
26335                 }
26336                 
26337             });
26338         });
26339         children.push(style);   
26340         
26341         btn('bold',false,true);
26342         btn('italic',false,true);
26343         btn('align-left', 'justifyleft',true);
26344         btn('align-center', 'justifycenter',true);
26345         btn('align-right' , 'justifyright',true);
26346         btn('link', false, false, function(btn) {
26347             //Roo.log("create link?");
26348             var url = prompt(this.createLinkText, this.defaultLinkValue);
26349             if(url && url != 'http:/'+'/'){
26350                 this.editorcore.relayCmd('createlink', url);
26351             }
26352         }),
26353         btn('list','insertunorderedlist',true);
26354         btn('pencil', false,true, function(btn){
26355                 Roo.log(this);
26356                 this.toggleSourceEdit(btn.pressed);
26357         });
26358         
26359         if (this.editor.btns.length > 0) {
26360             for (var i = 0; i<this.editor.btns.length; i++) {
26361                 children.push(this.editor.btns[i]);
26362             }
26363         }
26364         
26365         /*
26366         var cog = {
26367                 xtype: 'Button',
26368                 size : 'sm',
26369                 xns: Roo.bootstrap,
26370                 glyphicon : 'cog',
26371                 //html : 'submit'
26372                 menu : {
26373                     xtype: 'Menu',
26374                     xns: Roo.bootstrap,
26375                     items:  []
26376                 }
26377         };
26378         
26379         cog.menu.items.push({
26380             xtype :'MenuItem',
26381             xns: Roo.bootstrap,
26382             html : Clean styles,
26383             tagname : f,
26384             listeners : {
26385                 click : function()
26386                 {
26387                     editorcore.insertTag(this.tagname);
26388                     editor.focus();
26389                 }
26390             }
26391             
26392         });
26393        */
26394         
26395          
26396        this.xtype = 'NavSimplebar';
26397         
26398         for(var i=0;i< children.length;i++) {
26399             
26400             this.buttons.add(this.addxtypeChild(children[i]));
26401             
26402         }
26403         
26404         editor.on('editorevent', this.updateToolbar, this);
26405     },
26406     onBtnClick : function(id)
26407     {
26408        this.editorcore.relayCmd(id);
26409        this.editorcore.focus();
26410     },
26411     
26412     /**
26413      * Protected method that will not generally be called directly. It triggers
26414      * a toolbar update by reading the markup state of the current selection in the editor.
26415      */
26416     updateToolbar: function(){
26417
26418         if(!this.editorcore.activated){
26419             this.editor.onFirstFocus(); // is this neeed?
26420             return;
26421         }
26422
26423         var btns = this.buttons; 
26424         var doc = this.editorcore.doc;
26425         btns.get('bold').setActive(doc.queryCommandState('bold'));
26426         btns.get('italic').setActive(doc.queryCommandState('italic'));
26427         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26428         
26429         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26430         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26431         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26432         
26433         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26434         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26435          /*
26436         
26437         var ans = this.editorcore.getAllAncestors();
26438         if (this.formatCombo) {
26439             
26440             
26441             var store = this.formatCombo.store;
26442             this.formatCombo.setValue("");
26443             for (var i =0; i < ans.length;i++) {
26444                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26445                     // select it..
26446                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26447                     break;
26448                 }
26449             }
26450         }
26451         
26452         
26453         
26454         // hides menus... - so this cant be on a menu...
26455         Roo.bootstrap.MenuMgr.hideAll();
26456         */
26457         Roo.bootstrap.MenuMgr.hideAll();
26458         //this.editorsyncValue();
26459     },
26460     onFirstFocus: function() {
26461         this.buttons.each(function(item){
26462            item.enable();
26463         });
26464     },
26465     toggleSourceEdit : function(sourceEditMode){
26466         
26467           
26468         if(sourceEditMode){
26469             Roo.log("disabling buttons");
26470            this.buttons.each( function(item){
26471                 if(item.cmd != 'pencil'){
26472                     item.disable();
26473                 }
26474             });
26475           
26476         }else{
26477             Roo.log("enabling buttons");
26478             if(this.editorcore.initialized){
26479                 this.buttons.each( function(item){
26480                     item.enable();
26481                 });
26482             }
26483             
26484         }
26485         Roo.log("calling toggole on editor");
26486         // tell the editor that it's been pressed..
26487         this.editor.toggleSourceEdit(sourceEditMode);
26488        
26489     }
26490 });
26491
26492
26493
26494
26495  
26496 /*
26497  * - LGPL
26498  */
26499
26500 /**
26501  * @class Roo.bootstrap.Markdown
26502  * @extends Roo.bootstrap.TextArea
26503  * Bootstrap Showdown editable area
26504  * @cfg {string} content
26505  * 
26506  * @constructor
26507  * Create a new Showdown
26508  */
26509
26510 Roo.bootstrap.Markdown = function(config){
26511     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26512    
26513 };
26514
26515 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26516     
26517     editing :false,
26518     
26519     initEvents : function()
26520     {
26521         
26522         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26523         this.markdownEl = this.el.createChild({
26524             cls : 'roo-markdown-area'
26525         });
26526         this.inputEl().addClass('d-none');
26527         if (this.getValue() == '') {
26528             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26529             
26530         } else {
26531             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26532         }
26533         this.markdownEl.on('click', this.toggleTextEdit, this);
26534         this.on('blur', this.toggleTextEdit, this);
26535         this.on('specialkey', this.resizeTextArea, this);
26536     },
26537     
26538     toggleTextEdit : function()
26539     {
26540         var sh = this.markdownEl.getHeight();
26541         this.inputEl().addClass('d-none');
26542         this.markdownEl.addClass('d-none');
26543         if (!this.editing) {
26544             // show editor?
26545             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26546             this.inputEl().removeClass('d-none');
26547             this.inputEl().focus();
26548             this.editing = true;
26549             return;
26550         }
26551         // show showdown...
26552         this.updateMarkdown();
26553         this.markdownEl.removeClass('d-none');
26554         this.editing = false;
26555         return;
26556     },
26557     updateMarkdown : function()
26558     {
26559         if (this.getValue() == '') {
26560             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26561             return;
26562         }
26563  
26564         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26565     },
26566     
26567     resizeTextArea: function () {
26568         
26569         var sh = 100;
26570         Roo.log([sh, this.getValue().split("\n").length * 30]);
26571         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26572     },
26573     setValue : function(val)
26574     {
26575         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26576         if (!this.editing) {
26577             this.updateMarkdown();
26578         }
26579         
26580     },
26581     focus : function()
26582     {
26583         if (!this.editing) {
26584             this.toggleTextEdit();
26585         }
26586         
26587     }
26588
26589
26590 });
26591 /**
26592  * @class Roo.bootstrap.Table.AbstractSelectionModel
26593  * @extends Roo.util.Observable
26594  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26595  * implemented by descendant classes.  This class should not be directly instantiated.
26596  * @constructor
26597  */
26598 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26599     this.locked = false;
26600     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26601 };
26602
26603
26604 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26605     /** @ignore Called by the grid automatically. Do not call directly. */
26606     init : function(grid){
26607         this.grid = grid;
26608         this.initEvents();
26609     },
26610
26611     /**
26612      * Locks the selections.
26613      */
26614     lock : function(){
26615         this.locked = true;
26616     },
26617
26618     /**
26619      * Unlocks the selections.
26620      */
26621     unlock : function(){
26622         this.locked = false;
26623     },
26624
26625     /**
26626      * Returns true if the selections are locked.
26627      * @return {Boolean}
26628      */
26629     isLocked : function(){
26630         return this.locked;
26631     },
26632     
26633     
26634     initEvents : function ()
26635     {
26636         
26637     }
26638 });
26639 /**
26640  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26641  * @class Roo.bootstrap.Table.RowSelectionModel
26642  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26643  * It supports multiple selections and keyboard selection/navigation. 
26644  * @constructor
26645  * @param {Object} config
26646  */
26647
26648 Roo.bootstrap.Table.RowSelectionModel = function(config){
26649     Roo.apply(this, config);
26650     this.selections = new Roo.util.MixedCollection(false, function(o){
26651         return o.id;
26652     });
26653
26654     this.last = false;
26655     this.lastActive = false;
26656
26657     this.addEvents({
26658         /**
26659              * @event selectionchange
26660              * Fires when the selection changes
26661              * @param {SelectionModel} this
26662              */
26663             "selectionchange" : true,
26664         /**
26665              * @event afterselectionchange
26666              * Fires after the selection changes (eg. by key press or clicking)
26667              * @param {SelectionModel} this
26668              */
26669             "afterselectionchange" : true,
26670         /**
26671              * @event beforerowselect
26672              * Fires when a row is selected being selected, return false to cancel.
26673              * @param {SelectionModel} this
26674              * @param {Number} rowIndex The selected index
26675              * @param {Boolean} keepExisting False if other selections will be cleared
26676              */
26677             "beforerowselect" : true,
26678         /**
26679              * @event rowselect
26680              * Fires when a row is selected.
26681              * @param {SelectionModel} this
26682              * @param {Number} rowIndex The selected index
26683              * @param {Roo.data.Record} r The record
26684              */
26685             "rowselect" : true,
26686         /**
26687              * @event rowdeselect
26688              * Fires when a row is deselected.
26689              * @param {SelectionModel} this
26690              * @param {Number} rowIndex The selected index
26691              */
26692         "rowdeselect" : true
26693     });
26694     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26695     this.locked = false;
26696  };
26697
26698 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26699     /**
26700      * @cfg {Boolean} singleSelect
26701      * True to allow selection of only one row at a time (defaults to false)
26702      */
26703     singleSelect : false,
26704
26705     // private
26706     initEvents : function()
26707     {
26708
26709         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26710         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26711         //}else{ // allow click to work like normal
26712          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26713         //}
26714         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26715         this.grid.on("rowclick", this.handleMouseDown, this);
26716         
26717         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26718             "up" : function(e){
26719                 if(!e.shiftKey){
26720                     this.selectPrevious(e.shiftKey);
26721                 }else if(this.last !== false && this.lastActive !== false){
26722                     var last = this.last;
26723                     this.selectRange(this.last,  this.lastActive-1);
26724                     this.grid.getView().focusRow(this.lastActive);
26725                     if(last !== false){
26726                         this.last = last;
26727                     }
26728                 }else{
26729                     this.selectFirstRow();
26730                 }
26731                 this.fireEvent("afterselectionchange", this);
26732             },
26733             "down" : function(e){
26734                 if(!e.shiftKey){
26735                     this.selectNext(e.shiftKey);
26736                 }else if(this.last !== false && this.lastActive !== false){
26737                     var last = this.last;
26738                     this.selectRange(this.last,  this.lastActive+1);
26739                     this.grid.getView().focusRow(this.lastActive);
26740                     if(last !== false){
26741                         this.last = last;
26742                     }
26743                 }else{
26744                     this.selectFirstRow();
26745                 }
26746                 this.fireEvent("afterselectionchange", this);
26747             },
26748             scope: this
26749         });
26750         this.grid.store.on('load', function(){
26751             this.selections.clear();
26752         },this);
26753         /*
26754         var view = this.grid.view;
26755         view.on("refresh", this.onRefresh, this);
26756         view.on("rowupdated", this.onRowUpdated, this);
26757         view.on("rowremoved", this.onRemove, this);
26758         */
26759     },
26760
26761     // private
26762     onRefresh : function()
26763     {
26764         var ds = this.grid.store, i, v = this.grid.view;
26765         var s = this.selections;
26766         s.each(function(r){
26767             if((i = ds.indexOfId(r.id)) != -1){
26768                 v.onRowSelect(i);
26769             }else{
26770                 s.remove(r);
26771             }
26772         });
26773     },
26774
26775     // private
26776     onRemove : function(v, index, r){
26777         this.selections.remove(r);
26778     },
26779
26780     // private
26781     onRowUpdated : function(v, index, r){
26782         if(this.isSelected(r)){
26783             v.onRowSelect(index);
26784         }
26785     },
26786
26787     /**
26788      * Select records.
26789      * @param {Array} records The records to select
26790      * @param {Boolean} keepExisting (optional) True to keep existing selections
26791      */
26792     selectRecords : function(records, keepExisting)
26793     {
26794         if(!keepExisting){
26795             this.clearSelections();
26796         }
26797             var ds = this.grid.store;
26798         for(var i = 0, len = records.length; i < len; i++){
26799             this.selectRow(ds.indexOf(records[i]), true);
26800         }
26801     },
26802
26803     /**
26804      * Gets the number of selected rows.
26805      * @return {Number}
26806      */
26807     getCount : function(){
26808         return this.selections.length;
26809     },
26810
26811     /**
26812      * Selects the first row in the grid.
26813      */
26814     selectFirstRow : function(){
26815         this.selectRow(0);
26816     },
26817
26818     /**
26819      * Select the last row.
26820      * @param {Boolean} keepExisting (optional) True to keep existing selections
26821      */
26822     selectLastRow : function(keepExisting){
26823         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26824         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26825     },
26826
26827     /**
26828      * Selects the row immediately following the last selected row.
26829      * @param {Boolean} keepExisting (optional) True to keep existing selections
26830      */
26831     selectNext : function(keepExisting)
26832     {
26833             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26834             this.selectRow(this.last+1, keepExisting);
26835             this.grid.getView().focusRow(this.last);
26836         }
26837     },
26838
26839     /**
26840      * Selects the row that precedes the last selected row.
26841      * @param {Boolean} keepExisting (optional) True to keep existing selections
26842      */
26843     selectPrevious : function(keepExisting){
26844         if(this.last){
26845             this.selectRow(this.last-1, keepExisting);
26846             this.grid.getView().focusRow(this.last);
26847         }
26848     },
26849
26850     /**
26851      * Returns the selected records
26852      * @return {Array} Array of selected records
26853      */
26854     getSelections : function(){
26855         return [].concat(this.selections.items);
26856     },
26857
26858     /**
26859      * Returns the first selected record.
26860      * @return {Record}
26861      */
26862     getSelected : function(){
26863         return this.selections.itemAt(0);
26864     },
26865
26866
26867     /**
26868      * Clears all selections.
26869      */
26870     clearSelections : function(fast)
26871     {
26872         if(this.locked) {
26873             return;
26874         }
26875         if(fast !== true){
26876                 var ds = this.grid.store;
26877             var s = this.selections;
26878             s.each(function(r){
26879                 this.deselectRow(ds.indexOfId(r.id));
26880             }, this);
26881             s.clear();
26882         }else{
26883             this.selections.clear();
26884         }
26885         this.last = false;
26886     },
26887
26888
26889     /**
26890      * Selects all rows.
26891      */
26892     selectAll : function(){
26893         if(this.locked) {
26894             return;
26895         }
26896         this.selections.clear();
26897         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26898             this.selectRow(i, true);
26899         }
26900     },
26901
26902     /**
26903      * Returns True if there is a selection.
26904      * @return {Boolean}
26905      */
26906     hasSelection : function(){
26907         return this.selections.length > 0;
26908     },
26909
26910     /**
26911      * Returns True if the specified row is selected.
26912      * @param {Number/Record} record The record or index of the record to check
26913      * @return {Boolean}
26914      */
26915     isSelected : function(index){
26916             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26917         return (r && this.selections.key(r.id) ? true : false);
26918     },
26919
26920     /**
26921      * Returns True if the specified record id is selected.
26922      * @param {String} id The id of record to check
26923      * @return {Boolean}
26924      */
26925     isIdSelected : function(id){
26926         return (this.selections.key(id) ? true : false);
26927     },
26928
26929
26930     // private
26931     handleMouseDBClick : function(e, t){
26932         
26933     },
26934     // private
26935     handleMouseDown : function(e, t)
26936     {
26937             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26938         if(this.isLocked() || rowIndex < 0 ){
26939             return;
26940         };
26941         if(e.shiftKey && this.last !== false){
26942             var last = this.last;
26943             this.selectRange(last, rowIndex, e.ctrlKey);
26944             this.last = last; // reset the last
26945             t.focus();
26946     
26947         }else{
26948             var isSelected = this.isSelected(rowIndex);
26949             //Roo.log("select row:" + rowIndex);
26950             if(isSelected){
26951                 this.deselectRow(rowIndex);
26952             } else {
26953                         this.selectRow(rowIndex, true);
26954             }
26955     
26956             /*
26957                 if(e.button !== 0 && isSelected){
26958                 alert('rowIndex 2: ' + rowIndex);
26959                     view.focusRow(rowIndex);
26960                 }else if(e.ctrlKey && isSelected){
26961                     this.deselectRow(rowIndex);
26962                 }else if(!isSelected){
26963                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26964                     view.focusRow(rowIndex);
26965                 }
26966             */
26967         }
26968         this.fireEvent("afterselectionchange", this);
26969     },
26970     // private
26971     handleDragableRowClick :  function(grid, rowIndex, e) 
26972     {
26973         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26974             this.selectRow(rowIndex, false);
26975             grid.view.focusRow(rowIndex);
26976              this.fireEvent("afterselectionchange", this);
26977         }
26978     },
26979     
26980     /**
26981      * Selects multiple rows.
26982      * @param {Array} rows Array of the indexes of the row to select
26983      * @param {Boolean} keepExisting (optional) True to keep existing selections
26984      */
26985     selectRows : function(rows, keepExisting){
26986         if(!keepExisting){
26987             this.clearSelections();
26988         }
26989         for(var i = 0, len = rows.length; i < len; i++){
26990             this.selectRow(rows[i], true);
26991         }
26992     },
26993
26994     /**
26995      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26996      * @param {Number} startRow The index of the first row in the range
26997      * @param {Number} endRow The index of the last row in the range
26998      * @param {Boolean} keepExisting (optional) True to retain existing selections
26999      */
27000     selectRange : function(startRow, endRow, keepExisting){
27001         if(this.locked) {
27002             return;
27003         }
27004         if(!keepExisting){
27005             this.clearSelections();
27006         }
27007         if(startRow <= endRow){
27008             for(var i = startRow; i <= endRow; i++){
27009                 this.selectRow(i, true);
27010             }
27011         }else{
27012             for(var i = startRow; i >= endRow; i--){
27013                 this.selectRow(i, true);
27014             }
27015         }
27016     },
27017
27018     /**
27019      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27020      * @param {Number} startRow The index of the first row in the range
27021      * @param {Number} endRow The index of the last row in the range
27022      */
27023     deselectRange : function(startRow, endRow, preventViewNotify){
27024         if(this.locked) {
27025             return;
27026         }
27027         for(var i = startRow; i <= endRow; i++){
27028             this.deselectRow(i, preventViewNotify);
27029         }
27030     },
27031
27032     /**
27033      * Selects a row.
27034      * @param {Number} row The index of the row to select
27035      * @param {Boolean} keepExisting (optional) True to keep existing selections
27036      */
27037     selectRow : function(index, keepExisting, preventViewNotify)
27038     {
27039             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27040             return;
27041         }
27042         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27043             if(!keepExisting || this.singleSelect){
27044                 this.clearSelections();
27045             }
27046             
27047             var r = this.grid.store.getAt(index);
27048             //console.log('selectRow - record id :' + r.id);
27049             
27050             this.selections.add(r);
27051             this.last = this.lastActive = index;
27052             if(!preventViewNotify){
27053                 var proxy = new Roo.Element(
27054                                 this.grid.getRowDom(index)
27055                 );
27056                 proxy.addClass('bg-info info');
27057             }
27058             this.fireEvent("rowselect", this, index, r);
27059             this.fireEvent("selectionchange", this);
27060         }
27061     },
27062
27063     /**
27064      * Deselects a row.
27065      * @param {Number} row The index of the row to deselect
27066      */
27067     deselectRow : function(index, preventViewNotify)
27068     {
27069         if(this.locked) {
27070             return;
27071         }
27072         if(this.last == index){
27073             this.last = false;
27074         }
27075         if(this.lastActive == index){
27076             this.lastActive = false;
27077         }
27078         
27079         var r = this.grid.store.getAt(index);
27080         if (!r) {
27081             return;
27082         }
27083         
27084         this.selections.remove(r);
27085         //.console.log('deselectRow - record id :' + r.id);
27086         if(!preventViewNotify){
27087         
27088             var proxy = new Roo.Element(
27089                 this.grid.getRowDom(index)
27090             );
27091             proxy.removeClass('bg-info info');
27092         }
27093         this.fireEvent("rowdeselect", this, index);
27094         this.fireEvent("selectionchange", this);
27095     },
27096
27097     // private
27098     restoreLast : function(){
27099         if(this._last){
27100             this.last = this._last;
27101         }
27102     },
27103
27104     // private
27105     acceptsNav : function(row, col, cm){
27106         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27107     },
27108
27109     // private
27110     onEditorKey : function(field, e){
27111         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27112         if(k == e.TAB){
27113             e.stopEvent();
27114             ed.completeEdit();
27115             if(e.shiftKey){
27116                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27117             }else{
27118                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27119             }
27120         }else if(k == e.ENTER && !e.ctrlKey){
27121             e.stopEvent();
27122             ed.completeEdit();
27123             if(e.shiftKey){
27124                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27125             }else{
27126                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27127             }
27128         }else if(k == e.ESC){
27129             ed.cancelEdit();
27130         }
27131         if(newCell){
27132             g.startEditing(newCell[0], newCell[1]);
27133         }
27134     }
27135 });
27136 /*
27137  * Based on:
27138  * Ext JS Library 1.1.1
27139  * Copyright(c) 2006-2007, Ext JS, LLC.
27140  *
27141  * Originally Released Under LGPL - original licence link has changed is not relivant.
27142  *
27143  * Fork - LGPL
27144  * <script type="text/javascript">
27145  */
27146  
27147 /**
27148  * @class Roo.bootstrap.PagingToolbar
27149  * @extends Roo.bootstrap.NavSimplebar
27150  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27151  * @constructor
27152  * Create a new PagingToolbar
27153  * @param {Object} config The config object
27154  * @param {Roo.data.Store} store
27155  */
27156 Roo.bootstrap.PagingToolbar = function(config)
27157 {
27158     // old args format still supported... - xtype is prefered..
27159         // created from xtype...
27160     
27161     this.ds = config.dataSource;
27162     
27163     if (config.store && !this.ds) {
27164         this.store= Roo.factory(config.store, Roo.data);
27165         this.ds = this.store;
27166         this.ds.xmodule = this.xmodule || false;
27167     }
27168     
27169     this.toolbarItems = [];
27170     if (config.items) {
27171         this.toolbarItems = config.items;
27172     }
27173     
27174     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27175     
27176     this.cursor = 0;
27177     
27178     if (this.ds) { 
27179         this.bind(this.ds);
27180     }
27181     
27182     if (Roo.bootstrap.version == 4) {
27183         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27184     } else {
27185         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27186     }
27187     
27188 };
27189
27190 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27191     /**
27192      * @cfg {Roo.data.Store} dataSource
27193      * The underlying data store providing the paged data
27194      */
27195     /**
27196      * @cfg {String/HTMLElement/Element} container
27197      * container The id or element that will contain the toolbar
27198      */
27199     /**
27200      * @cfg {Boolean} displayInfo
27201      * True to display the displayMsg (defaults to false)
27202      */
27203     /**
27204      * @cfg {Number} pageSize
27205      * The number of records to display per page (defaults to 20)
27206      */
27207     pageSize: 20,
27208     /**
27209      * @cfg {String} displayMsg
27210      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27211      */
27212     displayMsg : 'Displaying {0} - {1} of {2}',
27213     /**
27214      * @cfg {String} emptyMsg
27215      * The message to display when no records are found (defaults to "No data to display")
27216      */
27217     emptyMsg : 'No data to display',
27218     /**
27219      * Customizable piece of the default paging text (defaults to "Page")
27220      * @type String
27221      */
27222     beforePageText : "Page",
27223     /**
27224      * Customizable piece of the default paging text (defaults to "of %0")
27225      * @type String
27226      */
27227     afterPageText : "of {0}",
27228     /**
27229      * Customizable piece of the default paging text (defaults to "First Page")
27230      * @type String
27231      */
27232     firstText : "First Page",
27233     /**
27234      * Customizable piece of the default paging text (defaults to "Previous Page")
27235      * @type String
27236      */
27237     prevText : "Previous Page",
27238     /**
27239      * Customizable piece of the default paging text (defaults to "Next Page")
27240      * @type String
27241      */
27242     nextText : "Next Page",
27243     /**
27244      * Customizable piece of the default paging text (defaults to "Last Page")
27245      * @type String
27246      */
27247     lastText : "Last Page",
27248     /**
27249      * Customizable piece of the default paging text (defaults to "Refresh")
27250      * @type String
27251      */
27252     refreshText : "Refresh",
27253
27254     buttons : false,
27255     // private
27256     onRender : function(ct, position) 
27257     {
27258         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27259         this.navgroup.parentId = this.id;
27260         this.navgroup.onRender(this.el, null);
27261         // add the buttons to the navgroup
27262         
27263         if(this.displayInfo){
27264             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27265             this.displayEl = this.el.select('.x-paging-info', true).first();
27266 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27267 //            this.displayEl = navel.el.select('span',true).first();
27268         }
27269         
27270         var _this = this;
27271         
27272         if(this.buttons){
27273             Roo.each(_this.buttons, function(e){ // this might need to use render????
27274                Roo.factory(e).render(_this.el);
27275             });
27276         }
27277             
27278         Roo.each(_this.toolbarItems, function(e) {
27279             _this.navgroup.addItem(e);
27280         });
27281         
27282         
27283         this.first = this.navgroup.addItem({
27284             tooltip: this.firstText,
27285             cls: "prev btn-outline-secondary",
27286             html : ' <i class="fa fa-step-backward"></i>',
27287             disabled: true,
27288             preventDefault: true,
27289             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27290         });
27291         
27292         this.prev =  this.navgroup.addItem({
27293             tooltip: this.prevText,
27294             cls: "prev btn-outline-secondary",
27295             html : ' <i class="fa fa-backward"></i>',
27296             disabled: true,
27297             preventDefault: true,
27298             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27299         });
27300     //this.addSeparator();
27301         
27302         
27303         var field = this.navgroup.addItem( {
27304             tagtype : 'span',
27305             cls : 'x-paging-position  btn-outline-secondary',
27306              disabled: true,
27307             html : this.beforePageText  +
27308                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27309                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27310          } ); //?? escaped?
27311         
27312         this.field = field.el.select('input', true).first();
27313         this.field.on("keydown", this.onPagingKeydown, this);
27314         this.field.on("focus", function(){this.dom.select();});
27315     
27316     
27317         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27318         //this.field.setHeight(18);
27319         //this.addSeparator();
27320         this.next = this.navgroup.addItem({
27321             tooltip: this.nextText,
27322             cls: "next btn-outline-secondary",
27323             html : ' <i class="fa fa-forward"></i>',
27324             disabled: true,
27325             preventDefault: true,
27326             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27327         });
27328         this.last = this.navgroup.addItem({
27329             tooltip: this.lastText,
27330             html : ' <i class="fa fa-step-forward"></i>',
27331             cls: "next btn-outline-secondary",
27332             disabled: true,
27333             preventDefault: true,
27334             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27335         });
27336     //this.addSeparator();
27337         this.loading = this.navgroup.addItem({
27338             tooltip: this.refreshText,
27339             cls: "btn-outline-secondary",
27340             html : ' <i class="fa fa-refresh"></i>',
27341             preventDefault: true,
27342             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27343         });
27344         
27345     },
27346
27347     // private
27348     updateInfo : function(){
27349         if(this.displayEl){
27350             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27351             var msg = count == 0 ?
27352                 this.emptyMsg :
27353                 String.format(
27354                     this.displayMsg,
27355                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27356                 );
27357             this.displayEl.update(msg);
27358         }
27359     },
27360
27361     // private
27362     onLoad : function(ds, r, o)
27363     {
27364         this.cursor = o.params && o.params.start ? o.params.start : 0;
27365         
27366         var d = this.getPageData(),
27367             ap = d.activePage,
27368             ps = d.pages;
27369         
27370         
27371         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27372         this.field.dom.value = ap;
27373         this.first.setDisabled(ap == 1);
27374         this.prev.setDisabled(ap == 1);
27375         this.next.setDisabled(ap == ps);
27376         this.last.setDisabled(ap == ps);
27377         this.loading.enable();
27378         this.updateInfo();
27379     },
27380
27381     // private
27382     getPageData : function(){
27383         var total = this.ds.getTotalCount();
27384         return {
27385             total : total,
27386             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27387             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27388         };
27389     },
27390
27391     // private
27392     onLoadError : function(){
27393         this.loading.enable();
27394     },
27395
27396     // private
27397     onPagingKeydown : function(e){
27398         var k = e.getKey();
27399         var d = this.getPageData();
27400         if(k == e.RETURN){
27401             var v = this.field.dom.value, pageNum;
27402             if(!v || isNaN(pageNum = parseInt(v, 10))){
27403                 this.field.dom.value = d.activePage;
27404                 return;
27405             }
27406             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27407             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27408             e.stopEvent();
27409         }
27410         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))
27411         {
27412           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27413           this.field.dom.value = pageNum;
27414           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27415           e.stopEvent();
27416         }
27417         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27418         {
27419           var v = this.field.dom.value, pageNum; 
27420           var increment = (e.shiftKey) ? 10 : 1;
27421           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27422                 increment *= -1;
27423           }
27424           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27425             this.field.dom.value = d.activePage;
27426             return;
27427           }
27428           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27429           {
27430             this.field.dom.value = parseInt(v, 10) + increment;
27431             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27432             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27433           }
27434           e.stopEvent();
27435         }
27436     },
27437
27438     // private
27439     beforeLoad : function(){
27440         if(this.loading){
27441             this.loading.disable();
27442         }
27443     },
27444
27445     // private
27446     onClick : function(which){
27447         
27448         var ds = this.ds;
27449         if (!ds) {
27450             return;
27451         }
27452         
27453         switch(which){
27454             case "first":
27455                 ds.load({params:{start: 0, limit: this.pageSize}});
27456             break;
27457             case "prev":
27458                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27459             break;
27460             case "next":
27461                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27462             break;
27463             case "last":
27464                 var total = ds.getTotalCount();
27465                 var extra = total % this.pageSize;
27466                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27467                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27468             break;
27469             case "refresh":
27470                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27471             break;
27472         }
27473     },
27474
27475     /**
27476      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27477      * @param {Roo.data.Store} store The data store to unbind
27478      */
27479     unbind : function(ds){
27480         ds.un("beforeload", this.beforeLoad, this);
27481         ds.un("load", this.onLoad, this);
27482         ds.un("loadexception", this.onLoadError, this);
27483         ds.un("remove", this.updateInfo, this);
27484         ds.un("add", this.updateInfo, this);
27485         this.ds = undefined;
27486     },
27487
27488     /**
27489      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27490      * @param {Roo.data.Store} store The data store to bind
27491      */
27492     bind : function(ds){
27493         ds.on("beforeload", this.beforeLoad, this);
27494         ds.on("load", this.onLoad, this);
27495         ds.on("loadexception", this.onLoadError, this);
27496         ds.on("remove", this.updateInfo, this);
27497         ds.on("add", this.updateInfo, this);
27498         this.ds = ds;
27499     }
27500 });/*
27501  * - LGPL
27502  *
27503  * element
27504  * 
27505  */
27506
27507 /**
27508  * @class Roo.bootstrap.MessageBar
27509  * @extends Roo.bootstrap.Component
27510  * Bootstrap MessageBar class
27511  * @cfg {String} html contents of the MessageBar
27512  * @cfg {String} weight (info | success | warning | danger) default info
27513  * @cfg {String} beforeClass insert the bar before the given class
27514  * @cfg {Boolean} closable (true | false) default false
27515  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27516  * 
27517  * @constructor
27518  * Create a new Element
27519  * @param {Object} config The config object
27520  */
27521
27522 Roo.bootstrap.MessageBar = function(config){
27523     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27524 };
27525
27526 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27527     
27528     html: '',
27529     weight: 'info',
27530     closable: false,
27531     fixed: false,
27532     beforeClass: 'bootstrap-sticky-wrap',
27533     
27534     getAutoCreate : function(){
27535         
27536         var cfg = {
27537             tag: 'div',
27538             cls: 'alert alert-dismissable alert-' + this.weight,
27539             cn: [
27540                 {
27541                     tag: 'span',
27542                     cls: 'message',
27543                     html: this.html || ''
27544                 }
27545             ]
27546         };
27547         
27548         if(this.fixed){
27549             cfg.cls += ' alert-messages-fixed';
27550         }
27551         
27552         if(this.closable){
27553             cfg.cn.push({
27554                 tag: 'button',
27555                 cls: 'close',
27556                 html: 'x'
27557             });
27558         }
27559         
27560         return cfg;
27561     },
27562     
27563     onRender : function(ct, position)
27564     {
27565         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27566         
27567         if(!this.el){
27568             var cfg = Roo.apply({},  this.getAutoCreate());
27569             cfg.id = Roo.id();
27570             
27571             if (this.cls) {
27572                 cfg.cls += ' ' + this.cls;
27573             }
27574             if (this.style) {
27575                 cfg.style = this.style;
27576             }
27577             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27578             
27579             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27580         }
27581         
27582         this.el.select('>button.close').on('click', this.hide, this);
27583         
27584     },
27585     
27586     show : function()
27587     {
27588         if (!this.rendered) {
27589             this.render();
27590         }
27591         
27592         this.el.show();
27593         
27594         this.fireEvent('show', this);
27595         
27596     },
27597     
27598     hide : function()
27599     {
27600         if (!this.rendered) {
27601             this.render();
27602         }
27603         
27604         this.el.hide();
27605         
27606         this.fireEvent('hide', this);
27607     },
27608     
27609     update : function()
27610     {
27611 //        var e = this.el.dom.firstChild;
27612 //        
27613 //        if(this.closable){
27614 //            e = e.nextSibling;
27615 //        }
27616 //        
27617 //        e.data = this.html || '';
27618
27619         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27620     }
27621    
27622 });
27623
27624  
27625
27626      /*
27627  * - LGPL
27628  *
27629  * Graph
27630  * 
27631  */
27632
27633
27634 /**
27635  * @class Roo.bootstrap.Graph
27636  * @extends Roo.bootstrap.Component
27637  * Bootstrap Graph class
27638 > Prameters
27639  -sm {number} sm 4
27640  -md {number} md 5
27641  @cfg {String} graphtype  bar | vbar | pie
27642  @cfg {number} g_x coodinator | centre x (pie)
27643  @cfg {number} g_y coodinator | centre y (pie)
27644  @cfg {number} g_r radius (pie)
27645  @cfg {number} g_height height of the chart (respected by all elements in the set)
27646  @cfg {number} g_width width of the chart (respected by all elements in the set)
27647  @cfg {Object} title The title of the chart
27648     
27649  -{Array}  values
27650  -opts (object) options for the chart 
27651      o {
27652      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27653      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27654      o vgutter (number)
27655      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.
27656      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27657      o to
27658      o stretch (boolean)
27659      o }
27660  -opts (object) options for the pie
27661      o{
27662      o cut
27663      o startAngle (number)
27664      o endAngle (number)
27665      } 
27666  *
27667  * @constructor
27668  * Create a new Input
27669  * @param {Object} config The config object
27670  */
27671
27672 Roo.bootstrap.Graph = function(config){
27673     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27674     
27675     this.addEvents({
27676         // img events
27677         /**
27678          * @event click
27679          * The img click event for the img.
27680          * @param {Roo.EventObject} e
27681          */
27682         "click" : true
27683     });
27684 };
27685
27686 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27687     
27688     sm: 4,
27689     md: 5,
27690     graphtype: 'bar',
27691     g_height: 250,
27692     g_width: 400,
27693     g_x: 50,
27694     g_y: 50,
27695     g_r: 30,
27696     opts:{
27697         //g_colors: this.colors,
27698         g_type: 'soft',
27699         g_gutter: '20%'
27700
27701     },
27702     title : false,
27703
27704     getAutoCreate : function(){
27705         
27706         var cfg = {
27707             tag: 'div',
27708             html : null
27709         };
27710         
27711         
27712         return  cfg;
27713     },
27714
27715     onRender : function(ct,position){
27716         
27717         
27718         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27719         
27720         if (typeof(Raphael) == 'undefined') {
27721             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27722             return;
27723         }
27724         
27725         this.raphael = Raphael(this.el.dom);
27726         
27727                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27728                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27729                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27730                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27731                 /*
27732                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27733                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27734                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27735                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27736                 
27737                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27738                 r.barchart(330, 10, 300, 220, data1);
27739                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27740                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27741                 */
27742                 
27743                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27744                 // r.barchart(30, 30, 560, 250,  xdata, {
27745                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27746                 //     axis : "0 0 1 1",
27747                 //     axisxlabels :  xdata
27748                 //     //yvalues : cols,
27749                    
27750                 // });
27751 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27752 //        
27753 //        this.load(null,xdata,{
27754 //                axis : "0 0 1 1",
27755 //                axisxlabels :  xdata
27756 //                });
27757
27758     },
27759
27760     load : function(graphtype,xdata,opts)
27761     {
27762         this.raphael.clear();
27763         if(!graphtype) {
27764             graphtype = this.graphtype;
27765         }
27766         if(!opts){
27767             opts = this.opts;
27768         }
27769         var r = this.raphael,
27770             fin = function () {
27771                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27772             },
27773             fout = function () {
27774                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27775             },
27776             pfin = function() {
27777                 this.sector.stop();
27778                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27779
27780                 if (this.label) {
27781                     this.label[0].stop();
27782                     this.label[0].attr({ r: 7.5 });
27783                     this.label[1].attr({ "font-weight": 800 });
27784                 }
27785             },
27786             pfout = function() {
27787                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27788
27789                 if (this.label) {
27790                     this.label[0].animate({ r: 5 }, 500, "bounce");
27791                     this.label[1].attr({ "font-weight": 400 });
27792                 }
27793             };
27794
27795         switch(graphtype){
27796             case 'bar':
27797                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27798                 break;
27799             case 'hbar':
27800                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27801                 break;
27802             case 'pie':
27803 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27804 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27805 //            
27806                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27807                 
27808                 break;
27809
27810         }
27811         
27812         if(this.title){
27813             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27814         }
27815         
27816     },
27817     
27818     setTitle: function(o)
27819     {
27820         this.title = o;
27821     },
27822     
27823     initEvents: function() {
27824         
27825         if(!this.href){
27826             this.el.on('click', this.onClick, this);
27827         }
27828     },
27829     
27830     onClick : function(e)
27831     {
27832         Roo.log('img onclick');
27833         this.fireEvent('click', this, e);
27834     }
27835    
27836 });
27837
27838  
27839 /*
27840  * - LGPL
27841  *
27842  * numberBox
27843  * 
27844  */
27845 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27846
27847 /**
27848  * @class Roo.bootstrap.dash.NumberBox
27849  * @extends Roo.bootstrap.Component
27850  * Bootstrap NumberBox class
27851  * @cfg {String} headline Box headline
27852  * @cfg {String} content Box content
27853  * @cfg {String} icon Box icon
27854  * @cfg {String} footer Footer text
27855  * @cfg {String} fhref Footer href
27856  * 
27857  * @constructor
27858  * Create a new NumberBox
27859  * @param {Object} config The config object
27860  */
27861
27862
27863 Roo.bootstrap.dash.NumberBox = function(config){
27864     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27865     
27866 };
27867
27868 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27869     
27870     headline : '',
27871     content : '',
27872     icon : '',
27873     footer : '',
27874     fhref : '',
27875     ficon : '',
27876     
27877     getAutoCreate : function(){
27878         
27879         var cfg = {
27880             tag : 'div',
27881             cls : 'small-box ',
27882             cn : [
27883                 {
27884                     tag : 'div',
27885                     cls : 'inner',
27886                     cn :[
27887                         {
27888                             tag : 'h3',
27889                             cls : 'roo-headline',
27890                             html : this.headline
27891                         },
27892                         {
27893                             tag : 'p',
27894                             cls : 'roo-content',
27895                             html : this.content
27896                         }
27897                     ]
27898                 }
27899             ]
27900         };
27901         
27902         if(this.icon){
27903             cfg.cn.push({
27904                 tag : 'div',
27905                 cls : 'icon',
27906                 cn :[
27907                     {
27908                         tag : 'i',
27909                         cls : 'ion ' + this.icon
27910                     }
27911                 ]
27912             });
27913         }
27914         
27915         if(this.footer){
27916             var footer = {
27917                 tag : 'a',
27918                 cls : 'small-box-footer',
27919                 href : this.fhref || '#',
27920                 html : this.footer
27921             };
27922             
27923             cfg.cn.push(footer);
27924             
27925         }
27926         
27927         return  cfg;
27928     },
27929
27930     onRender : function(ct,position){
27931         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27932
27933
27934        
27935                 
27936     },
27937
27938     setHeadline: function (value)
27939     {
27940         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27941     },
27942     
27943     setFooter: function (value, href)
27944     {
27945         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27946         
27947         if(href){
27948             this.el.select('a.small-box-footer',true).first().attr('href', href);
27949         }
27950         
27951     },
27952
27953     setContent: function (value)
27954     {
27955         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27956     },
27957
27958     initEvents: function() 
27959     {   
27960         
27961     }
27962     
27963 });
27964
27965  
27966 /*
27967  * - LGPL
27968  *
27969  * TabBox
27970  * 
27971  */
27972 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27973
27974 /**
27975  * @class Roo.bootstrap.dash.TabBox
27976  * @extends Roo.bootstrap.Component
27977  * Bootstrap TabBox class
27978  * @cfg {String} title Title of the TabBox
27979  * @cfg {String} icon Icon of the TabBox
27980  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27981  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27982  * 
27983  * @constructor
27984  * Create a new TabBox
27985  * @param {Object} config The config object
27986  */
27987
27988
27989 Roo.bootstrap.dash.TabBox = function(config){
27990     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27991     this.addEvents({
27992         // raw events
27993         /**
27994          * @event addpane
27995          * When a pane is added
27996          * @param {Roo.bootstrap.dash.TabPane} pane
27997          */
27998         "addpane" : true,
27999         /**
28000          * @event activatepane
28001          * When a pane is activated
28002          * @param {Roo.bootstrap.dash.TabPane} pane
28003          */
28004         "activatepane" : true
28005         
28006          
28007     });
28008     
28009     this.panes = [];
28010 };
28011
28012 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28013
28014     title : '',
28015     icon : false,
28016     showtabs : true,
28017     tabScrollable : false,
28018     
28019     getChildContainer : function()
28020     {
28021         return this.el.select('.tab-content', true).first();
28022     },
28023     
28024     getAutoCreate : function(){
28025         
28026         var header = {
28027             tag: 'li',
28028             cls: 'pull-left header',
28029             html: this.title,
28030             cn : []
28031         };
28032         
28033         if(this.icon){
28034             header.cn.push({
28035                 tag: 'i',
28036                 cls: 'fa ' + this.icon
28037             });
28038         }
28039         
28040         var h = {
28041             tag: 'ul',
28042             cls: 'nav nav-tabs pull-right',
28043             cn: [
28044                 header
28045             ]
28046         };
28047         
28048         if(this.tabScrollable){
28049             h = {
28050                 tag: 'div',
28051                 cls: 'tab-header',
28052                 cn: [
28053                     {
28054                         tag: 'ul',
28055                         cls: 'nav nav-tabs pull-right',
28056                         cn: [
28057                             header
28058                         ]
28059                     }
28060                 ]
28061             };
28062         }
28063         
28064         var cfg = {
28065             tag: 'div',
28066             cls: 'nav-tabs-custom',
28067             cn: [
28068                 h,
28069                 {
28070                     tag: 'div',
28071                     cls: 'tab-content no-padding',
28072                     cn: []
28073                 }
28074             ]
28075         };
28076
28077         return  cfg;
28078     },
28079     initEvents : function()
28080     {
28081         //Roo.log('add add pane handler');
28082         this.on('addpane', this.onAddPane, this);
28083     },
28084      /**
28085      * Updates the box title
28086      * @param {String} html to set the title to.
28087      */
28088     setTitle : function(value)
28089     {
28090         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28091     },
28092     onAddPane : function(pane)
28093     {
28094         this.panes.push(pane);
28095         //Roo.log('addpane');
28096         //Roo.log(pane);
28097         // tabs are rendere left to right..
28098         if(!this.showtabs){
28099             return;
28100         }
28101         
28102         var ctr = this.el.select('.nav-tabs', true).first();
28103          
28104          
28105         var existing = ctr.select('.nav-tab',true);
28106         var qty = existing.getCount();;
28107         
28108         
28109         var tab = ctr.createChild({
28110             tag : 'li',
28111             cls : 'nav-tab' + (qty ? '' : ' active'),
28112             cn : [
28113                 {
28114                     tag : 'a',
28115                     href:'#',
28116                     html : pane.title
28117                 }
28118             ]
28119         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28120         pane.tab = tab;
28121         
28122         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28123         if (!qty) {
28124             pane.el.addClass('active');
28125         }
28126         
28127                 
28128     },
28129     onTabClick : function(ev,un,ob,pane)
28130     {
28131         //Roo.log('tab - prev default');
28132         ev.preventDefault();
28133         
28134         
28135         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28136         pane.tab.addClass('active');
28137         //Roo.log(pane.title);
28138         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28139         // technically we should have a deactivate event.. but maybe add later.
28140         // and it should not de-activate the selected tab...
28141         this.fireEvent('activatepane', pane);
28142         pane.el.addClass('active');
28143         pane.fireEvent('activate');
28144         
28145         
28146     },
28147     
28148     getActivePane : function()
28149     {
28150         var r = false;
28151         Roo.each(this.panes, function(p) {
28152             if(p.el.hasClass('active')){
28153                 r = p;
28154                 return false;
28155             }
28156             
28157             return;
28158         });
28159         
28160         return r;
28161     }
28162     
28163     
28164 });
28165
28166  
28167 /*
28168  * - LGPL
28169  *
28170  * Tab pane
28171  * 
28172  */
28173 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28174 /**
28175  * @class Roo.bootstrap.TabPane
28176  * @extends Roo.bootstrap.Component
28177  * Bootstrap TabPane class
28178  * @cfg {Boolean} active (false | true) Default false
28179  * @cfg {String} title title of panel
28180
28181  * 
28182  * @constructor
28183  * Create a new TabPane
28184  * @param {Object} config The config object
28185  */
28186
28187 Roo.bootstrap.dash.TabPane = function(config){
28188     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28189     
28190     this.addEvents({
28191         // raw events
28192         /**
28193          * @event activate
28194          * When a pane is activated
28195          * @param {Roo.bootstrap.dash.TabPane} pane
28196          */
28197         "activate" : true
28198          
28199     });
28200 };
28201
28202 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28203     
28204     active : false,
28205     title : '',
28206     
28207     // the tabBox that this is attached to.
28208     tab : false,
28209      
28210     getAutoCreate : function() 
28211     {
28212         var cfg = {
28213             tag: 'div',
28214             cls: 'tab-pane'
28215         };
28216         
28217         if(this.active){
28218             cfg.cls += ' active';
28219         }
28220         
28221         return cfg;
28222     },
28223     initEvents  : function()
28224     {
28225         //Roo.log('trigger add pane handler');
28226         this.parent().fireEvent('addpane', this)
28227     },
28228     
28229      /**
28230      * Updates the tab title 
28231      * @param {String} html to set the title to.
28232      */
28233     setTitle: function(str)
28234     {
28235         if (!this.tab) {
28236             return;
28237         }
28238         this.title = str;
28239         this.tab.select('a', true).first().dom.innerHTML = str;
28240         
28241     }
28242     
28243     
28244     
28245 });
28246
28247  
28248
28249
28250  /*
28251  * - LGPL
28252  *
28253  * menu
28254  * 
28255  */
28256 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28257
28258 /**
28259  * @class Roo.bootstrap.menu.Menu
28260  * @extends Roo.bootstrap.Component
28261  * Bootstrap Menu class - container for Menu
28262  * @cfg {String} html Text of the menu
28263  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28264  * @cfg {String} icon Font awesome icon
28265  * @cfg {String} pos Menu align to (top | bottom) default bottom
28266  * 
28267  * 
28268  * @constructor
28269  * Create a new Menu
28270  * @param {Object} config The config object
28271  */
28272
28273
28274 Roo.bootstrap.menu.Menu = function(config){
28275     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28276     
28277     this.addEvents({
28278         /**
28279          * @event beforeshow
28280          * Fires before this menu is displayed
28281          * @param {Roo.bootstrap.menu.Menu} this
28282          */
28283         beforeshow : true,
28284         /**
28285          * @event beforehide
28286          * Fires before this menu is hidden
28287          * @param {Roo.bootstrap.menu.Menu} this
28288          */
28289         beforehide : true,
28290         /**
28291          * @event show
28292          * Fires after this menu is displayed
28293          * @param {Roo.bootstrap.menu.Menu} this
28294          */
28295         show : true,
28296         /**
28297          * @event hide
28298          * Fires after this menu is hidden
28299          * @param {Roo.bootstrap.menu.Menu} this
28300          */
28301         hide : true,
28302         /**
28303          * @event click
28304          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28305          * @param {Roo.bootstrap.menu.Menu} this
28306          * @param {Roo.EventObject} e
28307          */
28308         click : true
28309     });
28310     
28311 };
28312
28313 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28314     
28315     submenu : false,
28316     html : '',
28317     weight : 'default',
28318     icon : false,
28319     pos : 'bottom',
28320     
28321     
28322     getChildContainer : function() {
28323         if(this.isSubMenu){
28324             return this.el;
28325         }
28326         
28327         return this.el.select('ul.dropdown-menu', true).first();  
28328     },
28329     
28330     getAutoCreate : function()
28331     {
28332         var text = [
28333             {
28334                 tag : 'span',
28335                 cls : 'roo-menu-text',
28336                 html : this.html
28337             }
28338         ];
28339         
28340         if(this.icon){
28341             text.unshift({
28342                 tag : 'i',
28343                 cls : 'fa ' + this.icon
28344             })
28345         }
28346         
28347         
28348         var cfg = {
28349             tag : 'div',
28350             cls : 'btn-group',
28351             cn : [
28352                 {
28353                     tag : 'button',
28354                     cls : 'dropdown-button btn btn-' + this.weight,
28355                     cn : text
28356                 },
28357                 {
28358                     tag : 'button',
28359                     cls : 'dropdown-toggle btn btn-' + this.weight,
28360                     cn : [
28361                         {
28362                             tag : 'span',
28363                             cls : 'caret'
28364                         }
28365                     ]
28366                 },
28367                 {
28368                     tag : 'ul',
28369                     cls : 'dropdown-menu'
28370                 }
28371             ]
28372             
28373         };
28374         
28375         if(this.pos == 'top'){
28376             cfg.cls += ' dropup';
28377         }
28378         
28379         if(this.isSubMenu){
28380             cfg = {
28381                 tag : 'ul',
28382                 cls : 'dropdown-menu'
28383             }
28384         }
28385         
28386         return cfg;
28387     },
28388     
28389     onRender : function(ct, position)
28390     {
28391         this.isSubMenu = ct.hasClass('dropdown-submenu');
28392         
28393         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28394     },
28395     
28396     initEvents : function() 
28397     {
28398         if(this.isSubMenu){
28399             return;
28400         }
28401         
28402         this.hidden = true;
28403         
28404         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28405         this.triggerEl.on('click', this.onTriggerPress, this);
28406         
28407         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28408         this.buttonEl.on('click', this.onClick, this);
28409         
28410     },
28411     
28412     list : function()
28413     {
28414         if(this.isSubMenu){
28415             return this.el;
28416         }
28417         
28418         return this.el.select('ul.dropdown-menu', true).first();
28419     },
28420     
28421     onClick : function(e)
28422     {
28423         this.fireEvent("click", this, e);
28424     },
28425     
28426     onTriggerPress  : function(e)
28427     {   
28428         if (this.isVisible()) {
28429             this.hide();
28430         } else {
28431             this.show();
28432         }
28433     },
28434     
28435     isVisible : function(){
28436         return !this.hidden;
28437     },
28438     
28439     show : function()
28440     {
28441         this.fireEvent("beforeshow", this);
28442         
28443         this.hidden = false;
28444         this.el.addClass('open');
28445         
28446         Roo.get(document).on("mouseup", this.onMouseUp, this);
28447         
28448         this.fireEvent("show", this);
28449         
28450         
28451     },
28452     
28453     hide : function()
28454     {
28455         this.fireEvent("beforehide", this);
28456         
28457         this.hidden = true;
28458         this.el.removeClass('open');
28459         
28460         Roo.get(document).un("mouseup", this.onMouseUp);
28461         
28462         this.fireEvent("hide", this);
28463     },
28464     
28465     onMouseUp : function()
28466     {
28467         this.hide();
28468     }
28469     
28470 });
28471
28472  
28473  /*
28474  * - LGPL
28475  *
28476  * menu item
28477  * 
28478  */
28479 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28480
28481 /**
28482  * @class Roo.bootstrap.menu.Item
28483  * @extends Roo.bootstrap.Component
28484  * Bootstrap MenuItem class
28485  * @cfg {Boolean} submenu (true | false) default false
28486  * @cfg {String} html text of the item
28487  * @cfg {String} href the link
28488  * @cfg {Boolean} disable (true | false) default false
28489  * @cfg {Boolean} preventDefault (true | false) default true
28490  * @cfg {String} icon Font awesome icon
28491  * @cfg {String} pos Submenu align to (left | right) default right 
28492  * 
28493  * 
28494  * @constructor
28495  * Create a new Item
28496  * @param {Object} config The config object
28497  */
28498
28499
28500 Roo.bootstrap.menu.Item = function(config){
28501     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28502     this.addEvents({
28503         /**
28504          * @event mouseover
28505          * Fires when the mouse is hovering over this menu
28506          * @param {Roo.bootstrap.menu.Item} this
28507          * @param {Roo.EventObject} e
28508          */
28509         mouseover : true,
28510         /**
28511          * @event mouseout
28512          * Fires when the mouse exits this menu
28513          * @param {Roo.bootstrap.menu.Item} this
28514          * @param {Roo.EventObject} e
28515          */
28516         mouseout : true,
28517         // raw events
28518         /**
28519          * @event click
28520          * The raw click event for the entire grid.
28521          * @param {Roo.EventObject} e
28522          */
28523         click : true
28524     });
28525 };
28526
28527 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28528     
28529     submenu : false,
28530     href : '',
28531     html : '',
28532     preventDefault: true,
28533     disable : false,
28534     icon : false,
28535     pos : 'right',
28536     
28537     getAutoCreate : function()
28538     {
28539         var text = [
28540             {
28541                 tag : 'span',
28542                 cls : 'roo-menu-item-text',
28543                 html : this.html
28544             }
28545         ];
28546         
28547         if(this.icon){
28548             text.unshift({
28549                 tag : 'i',
28550                 cls : 'fa ' + this.icon
28551             })
28552         }
28553         
28554         var cfg = {
28555             tag : 'li',
28556             cn : [
28557                 {
28558                     tag : 'a',
28559                     href : this.href || '#',
28560                     cn : text
28561                 }
28562             ]
28563         };
28564         
28565         if(this.disable){
28566             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28567         }
28568         
28569         if(this.submenu){
28570             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28571             
28572             if(this.pos == 'left'){
28573                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28574             }
28575         }
28576         
28577         return cfg;
28578     },
28579     
28580     initEvents : function() 
28581     {
28582         this.el.on('mouseover', this.onMouseOver, this);
28583         this.el.on('mouseout', this.onMouseOut, this);
28584         
28585         this.el.select('a', true).first().on('click', this.onClick, this);
28586         
28587     },
28588     
28589     onClick : function(e)
28590     {
28591         if(this.preventDefault){
28592             e.preventDefault();
28593         }
28594         
28595         this.fireEvent("click", this, e);
28596     },
28597     
28598     onMouseOver : function(e)
28599     {
28600         if(this.submenu && this.pos == 'left'){
28601             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28602         }
28603         
28604         this.fireEvent("mouseover", this, e);
28605     },
28606     
28607     onMouseOut : function(e)
28608     {
28609         this.fireEvent("mouseout", this, e);
28610     }
28611 });
28612
28613  
28614
28615  /*
28616  * - LGPL
28617  *
28618  * menu separator
28619  * 
28620  */
28621 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28622
28623 /**
28624  * @class Roo.bootstrap.menu.Separator
28625  * @extends Roo.bootstrap.Component
28626  * Bootstrap Separator class
28627  * 
28628  * @constructor
28629  * Create a new Separator
28630  * @param {Object} config The config object
28631  */
28632
28633
28634 Roo.bootstrap.menu.Separator = function(config){
28635     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28636 };
28637
28638 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28639     
28640     getAutoCreate : function(){
28641         var cfg = {
28642             tag : 'li',
28643             cls: 'divider'
28644         };
28645         
28646         return cfg;
28647     }
28648    
28649 });
28650
28651  
28652
28653  /*
28654  * - LGPL
28655  *
28656  * Tooltip
28657  * 
28658  */
28659
28660 /**
28661  * @class Roo.bootstrap.Tooltip
28662  * Bootstrap Tooltip class
28663  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28664  * to determine which dom element triggers the tooltip.
28665  * 
28666  * It needs to add support for additional attributes like tooltip-position
28667  * 
28668  * @constructor
28669  * Create a new Toolti
28670  * @param {Object} config The config object
28671  */
28672
28673 Roo.bootstrap.Tooltip = function(config){
28674     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28675     
28676     this.alignment = Roo.bootstrap.Tooltip.alignment;
28677     
28678     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28679         this.alignment = config.alignment;
28680     }
28681     
28682 };
28683
28684 Roo.apply(Roo.bootstrap.Tooltip, {
28685     /**
28686      * @function init initialize tooltip monitoring.
28687      * @static
28688      */
28689     currentEl : false,
28690     currentTip : false,
28691     currentRegion : false,
28692     
28693     //  init : delay?
28694     
28695     init : function()
28696     {
28697         Roo.get(document).on('mouseover', this.enter ,this);
28698         Roo.get(document).on('mouseout', this.leave, this);
28699          
28700         
28701         this.currentTip = new Roo.bootstrap.Tooltip();
28702     },
28703     
28704     enter : function(ev)
28705     {
28706         var dom = ev.getTarget();
28707         
28708         //Roo.log(['enter',dom]);
28709         var el = Roo.fly(dom);
28710         if (this.currentEl) {
28711             //Roo.log(dom);
28712             //Roo.log(this.currentEl);
28713             //Roo.log(this.currentEl.contains(dom));
28714             if (this.currentEl == el) {
28715                 return;
28716             }
28717             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28718                 return;
28719             }
28720
28721         }
28722         
28723         if (this.currentTip.el) {
28724             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28725         }    
28726         //Roo.log(ev);
28727         
28728         if(!el || el.dom == document){
28729             return;
28730         }
28731         
28732         var bindEl = el;
28733         
28734         // you can not look for children, as if el is the body.. then everythign is the child..
28735         if (!el.attr('tooltip')) { //
28736             if (!el.select("[tooltip]").elements.length) {
28737                 return;
28738             }
28739             // is the mouse over this child...?
28740             bindEl = el.select("[tooltip]").first();
28741             var xy = ev.getXY();
28742             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28743                 //Roo.log("not in region.");
28744                 return;
28745             }
28746             //Roo.log("child element over..");
28747             
28748         }
28749         this.currentEl = bindEl;
28750         this.currentTip.bind(bindEl);
28751         this.currentRegion = Roo.lib.Region.getRegion(dom);
28752         this.currentTip.enter();
28753         
28754     },
28755     leave : function(ev)
28756     {
28757         var dom = ev.getTarget();
28758         //Roo.log(['leave',dom]);
28759         if (!this.currentEl) {
28760             return;
28761         }
28762         
28763         
28764         if (dom != this.currentEl.dom) {
28765             return;
28766         }
28767         var xy = ev.getXY();
28768         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28769             return;
28770         }
28771         // only activate leave if mouse cursor is outside... bounding box..
28772         
28773         
28774         
28775         
28776         if (this.currentTip) {
28777             this.currentTip.leave();
28778         }
28779         //Roo.log('clear currentEl');
28780         this.currentEl = false;
28781         
28782         
28783     },
28784     alignment : {
28785         'left' : ['r-l', [-2,0], 'right'],
28786         'right' : ['l-r', [2,0], 'left'],
28787         'bottom' : ['t-b', [0,2], 'top'],
28788         'top' : [ 'b-t', [0,-2], 'bottom']
28789     }
28790     
28791 });
28792
28793
28794 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28795     
28796     
28797     bindEl : false,
28798     
28799     delay : null, // can be { show : 300 , hide: 500}
28800     
28801     timeout : null,
28802     
28803     hoverState : null, //???
28804     
28805     placement : 'bottom', 
28806     
28807     alignment : false,
28808     
28809     getAutoCreate : function(){
28810     
28811         var cfg = {
28812            cls : 'tooltip',   
28813            role : 'tooltip',
28814            cn : [
28815                 {
28816                     cls : 'tooltip-arrow arrow'
28817                 },
28818                 {
28819                     cls : 'tooltip-inner'
28820                 }
28821            ]
28822         };
28823         
28824         return cfg;
28825     },
28826     bind : function(el)
28827     {
28828         this.bindEl = el;
28829     },
28830     
28831     initEvents : function()
28832     {
28833         this.arrowEl = this.el.select('.arrow', true).first();
28834         this.innerEl = this.el.select('.tooltip-inner', true).first();
28835     },
28836     
28837     enter : function () {
28838        
28839         if (this.timeout != null) {
28840             clearTimeout(this.timeout);
28841         }
28842         
28843         this.hoverState = 'in';
28844          //Roo.log("enter - show");
28845         if (!this.delay || !this.delay.show) {
28846             this.show();
28847             return;
28848         }
28849         var _t = this;
28850         this.timeout = setTimeout(function () {
28851             if (_t.hoverState == 'in') {
28852                 _t.show();
28853             }
28854         }, this.delay.show);
28855     },
28856     leave : function()
28857     {
28858         clearTimeout(this.timeout);
28859     
28860         this.hoverState = 'out';
28861          if (!this.delay || !this.delay.hide) {
28862             this.hide();
28863             return;
28864         }
28865        
28866         var _t = this;
28867         this.timeout = setTimeout(function () {
28868             //Roo.log("leave - timeout");
28869             
28870             if (_t.hoverState == 'out') {
28871                 _t.hide();
28872                 Roo.bootstrap.Tooltip.currentEl = false;
28873             }
28874         }, delay);
28875     },
28876     
28877     show : function (msg)
28878     {
28879         if (!this.el) {
28880             this.render(document.body);
28881         }
28882         // set content.
28883         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28884         
28885         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28886         
28887         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28888         
28889         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28890                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28891         
28892         var placement = typeof this.placement == 'function' ?
28893             this.placement.call(this, this.el, on_el) :
28894             this.placement;
28895             
28896         var autoToken = /\s?auto?\s?/i;
28897         var autoPlace = autoToken.test(placement);
28898         if (autoPlace) {
28899             placement = placement.replace(autoToken, '') || 'top';
28900         }
28901         
28902         //this.el.detach()
28903         //this.el.setXY([0,0]);
28904         this.el.show();
28905         //this.el.dom.style.display='block';
28906         
28907         //this.el.appendTo(on_el);
28908         
28909         var p = this.getPosition();
28910         var box = this.el.getBox();
28911         
28912         if (autoPlace) {
28913             // fixme..
28914         }
28915         
28916         var align = this.alignment[placement];
28917         
28918         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28919         
28920         if(placement == 'top' || placement == 'bottom'){
28921             if(xy[0] < 0){
28922                 placement = 'right';
28923             }
28924             
28925             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28926                 placement = 'left';
28927             }
28928             
28929             var scroll = Roo.select('body', true).first().getScroll();
28930             
28931             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28932                 placement = 'top';
28933             }
28934             
28935             align = this.alignment[placement];
28936             
28937             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28938             
28939         }
28940         
28941         this.el.alignTo(this.bindEl, align[0],align[1]);
28942         //var arrow = this.el.select('.arrow',true).first();
28943         //arrow.set(align[2], 
28944         
28945         this.el.addClass(placement);
28946         this.el.addClass("bs-tooltip-"+ placement);
28947         
28948         this.el.addClass('in fade show');
28949         
28950         this.hoverState = null;
28951         
28952         if (this.el.hasClass('fade')) {
28953             // fade it?
28954         }
28955         
28956         
28957         
28958         
28959         
28960     },
28961     hide : function()
28962     {
28963          
28964         if (!this.el) {
28965             return;
28966         }
28967         //this.el.setXY([0,0]);
28968         this.el.removeClass(['show', 'in']);
28969         //this.el.hide();
28970         
28971     }
28972     
28973 });
28974  
28975
28976  /*
28977  * - LGPL
28978  *
28979  * Location Picker
28980  * 
28981  */
28982
28983 /**
28984  * @class Roo.bootstrap.LocationPicker
28985  * @extends Roo.bootstrap.Component
28986  * Bootstrap LocationPicker class
28987  * @cfg {Number} latitude Position when init default 0
28988  * @cfg {Number} longitude Position when init default 0
28989  * @cfg {Number} zoom default 15
28990  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28991  * @cfg {Boolean} mapTypeControl default false
28992  * @cfg {Boolean} disableDoubleClickZoom default false
28993  * @cfg {Boolean} scrollwheel default true
28994  * @cfg {Boolean} streetViewControl default false
28995  * @cfg {Number} radius default 0
28996  * @cfg {String} locationName
28997  * @cfg {Boolean} draggable default true
28998  * @cfg {Boolean} enableAutocomplete default false
28999  * @cfg {Boolean} enableReverseGeocode default true
29000  * @cfg {String} markerTitle
29001  * 
29002  * @constructor
29003  * Create a new LocationPicker
29004  * @param {Object} config The config object
29005  */
29006
29007
29008 Roo.bootstrap.LocationPicker = function(config){
29009     
29010     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29011     
29012     this.addEvents({
29013         /**
29014          * @event initial
29015          * Fires when the picker initialized.
29016          * @param {Roo.bootstrap.LocationPicker} this
29017          * @param {Google Location} location
29018          */
29019         initial : true,
29020         /**
29021          * @event positionchanged
29022          * Fires when the picker position changed.
29023          * @param {Roo.bootstrap.LocationPicker} this
29024          * @param {Google Location} location
29025          */
29026         positionchanged : true,
29027         /**
29028          * @event resize
29029          * Fires when the map resize.
29030          * @param {Roo.bootstrap.LocationPicker} this
29031          */
29032         resize : true,
29033         /**
29034          * @event show
29035          * Fires when the map show.
29036          * @param {Roo.bootstrap.LocationPicker} this
29037          */
29038         show : true,
29039         /**
29040          * @event hide
29041          * Fires when the map hide.
29042          * @param {Roo.bootstrap.LocationPicker} this
29043          */
29044         hide : true,
29045         /**
29046          * @event mapClick
29047          * Fires when click the map.
29048          * @param {Roo.bootstrap.LocationPicker} this
29049          * @param {Map event} e
29050          */
29051         mapClick : true,
29052         /**
29053          * @event mapRightClick
29054          * Fires when right click the map.
29055          * @param {Roo.bootstrap.LocationPicker} this
29056          * @param {Map event} e
29057          */
29058         mapRightClick : true,
29059         /**
29060          * @event markerClick
29061          * Fires when click the marker.
29062          * @param {Roo.bootstrap.LocationPicker} this
29063          * @param {Map event} e
29064          */
29065         markerClick : true,
29066         /**
29067          * @event markerRightClick
29068          * Fires when right click the marker.
29069          * @param {Roo.bootstrap.LocationPicker} this
29070          * @param {Map event} e
29071          */
29072         markerRightClick : true,
29073         /**
29074          * @event OverlayViewDraw
29075          * Fires when OverlayView Draw
29076          * @param {Roo.bootstrap.LocationPicker} this
29077          */
29078         OverlayViewDraw : true,
29079         /**
29080          * @event OverlayViewOnAdd
29081          * Fires when OverlayView Draw
29082          * @param {Roo.bootstrap.LocationPicker} this
29083          */
29084         OverlayViewOnAdd : true,
29085         /**
29086          * @event OverlayViewOnRemove
29087          * Fires when OverlayView Draw
29088          * @param {Roo.bootstrap.LocationPicker} this
29089          */
29090         OverlayViewOnRemove : true,
29091         /**
29092          * @event OverlayViewShow
29093          * Fires when OverlayView Draw
29094          * @param {Roo.bootstrap.LocationPicker} this
29095          * @param {Pixel} cpx
29096          */
29097         OverlayViewShow : true,
29098         /**
29099          * @event OverlayViewHide
29100          * Fires when OverlayView Draw
29101          * @param {Roo.bootstrap.LocationPicker} this
29102          */
29103         OverlayViewHide : true,
29104         /**
29105          * @event loadexception
29106          * Fires when load google lib failed.
29107          * @param {Roo.bootstrap.LocationPicker} this
29108          */
29109         loadexception : true
29110     });
29111         
29112 };
29113
29114 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29115     
29116     gMapContext: false,
29117     
29118     latitude: 0,
29119     longitude: 0,
29120     zoom: 15,
29121     mapTypeId: false,
29122     mapTypeControl: false,
29123     disableDoubleClickZoom: false,
29124     scrollwheel: true,
29125     streetViewControl: false,
29126     radius: 0,
29127     locationName: '',
29128     draggable: true,
29129     enableAutocomplete: false,
29130     enableReverseGeocode: true,
29131     markerTitle: '',
29132     
29133     getAutoCreate: function()
29134     {
29135
29136         var cfg = {
29137             tag: 'div',
29138             cls: 'roo-location-picker'
29139         };
29140         
29141         return cfg
29142     },
29143     
29144     initEvents: function(ct, position)
29145     {       
29146         if(!this.el.getWidth() || this.isApplied()){
29147             return;
29148         }
29149         
29150         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29151         
29152         this.initial();
29153     },
29154     
29155     initial: function()
29156     {
29157         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29158             this.fireEvent('loadexception', this);
29159             return;
29160         }
29161         
29162         if(!this.mapTypeId){
29163             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29164         }
29165         
29166         this.gMapContext = this.GMapContext();
29167         
29168         this.initOverlayView();
29169         
29170         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29171         
29172         var _this = this;
29173                 
29174         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29175             _this.setPosition(_this.gMapContext.marker.position);
29176         });
29177         
29178         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29179             _this.fireEvent('mapClick', this, event);
29180             
29181         });
29182
29183         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29184             _this.fireEvent('mapRightClick', this, event);
29185             
29186         });
29187         
29188         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29189             _this.fireEvent('markerClick', this, event);
29190             
29191         });
29192
29193         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29194             _this.fireEvent('markerRightClick', this, event);
29195             
29196         });
29197         
29198         this.setPosition(this.gMapContext.location);
29199         
29200         this.fireEvent('initial', this, this.gMapContext.location);
29201     },
29202     
29203     initOverlayView: function()
29204     {
29205         var _this = this;
29206         
29207         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29208             
29209             draw: function()
29210             {
29211                 _this.fireEvent('OverlayViewDraw', _this);
29212             },
29213             
29214             onAdd: function()
29215             {
29216                 _this.fireEvent('OverlayViewOnAdd', _this);
29217             },
29218             
29219             onRemove: function()
29220             {
29221                 _this.fireEvent('OverlayViewOnRemove', _this);
29222             },
29223             
29224             show: function(cpx)
29225             {
29226                 _this.fireEvent('OverlayViewShow', _this, cpx);
29227             },
29228             
29229             hide: function()
29230             {
29231                 _this.fireEvent('OverlayViewHide', _this);
29232             }
29233             
29234         });
29235     },
29236     
29237     fromLatLngToContainerPixel: function(event)
29238     {
29239         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29240     },
29241     
29242     isApplied: function() 
29243     {
29244         return this.getGmapContext() == false ? false : true;
29245     },
29246     
29247     getGmapContext: function() 
29248     {
29249         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29250     },
29251     
29252     GMapContext: function() 
29253     {
29254         var position = new google.maps.LatLng(this.latitude, this.longitude);
29255         
29256         var _map = new google.maps.Map(this.el.dom, {
29257             center: position,
29258             zoom: this.zoom,
29259             mapTypeId: this.mapTypeId,
29260             mapTypeControl: this.mapTypeControl,
29261             disableDoubleClickZoom: this.disableDoubleClickZoom,
29262             scrollwheel: this.scrollwheel,
29263             streetViewControl: this.streetViewControl,
29264             locationName: this.locationName,
29265             draggable: this.draggable,
29266             enableAutocomplete: this.enableAutocomplete,
29267             enableReverseGeocode: this.enableReverseGeocode
29268         });
29269         
29270         var _marker = new google.maps.Marker({
29271             position: position,
29272             map: _map,
29273             title: this.markerTitle,
29274             draggable: this.draggable
29275         });
29276         
29277         return {
29278             map: _map,
29279             marker: _marker,
29280             circle: null,
29281             location: position,
29282             radius: this.radius,
29283             locationName: this.locationName,
29284             addressComponents: {
29285                 formatted_address: null,
29286                 addressLine1: null,
29287                 addressLine2: null,
29288                 streetName: null,
29289                 streetNumber: null,
29290                 city: null,
29291                 district: null,
29292                 state: null,
29293                 stateOrProvince: null
29294             },
29295             settings: this,
29296             domContainer: this.el.dom,
29297             geodecoder: new google.maps.Geocoder()
29298         };
29299     },
29300     
29301     drawCircle: function(center, radius, options) 
29302     {
29303         if (this.gMapContext.circle != null) {
29304             this.gMapContext.circle.setMap(null);
29305         }
29306         if (radius > 0) {
29307             radius *= 1;
29308             options = Roo.apply({}, options, {
29309                 strokeColor: "#0000FF",
29310                 strokeOpacity: .35,
29311                 strokeWeight: 2,
29312                 fillColor: "#0000FF",
29313                 fillOpacity: .2
29314             });
29315             
29316             options.map = this.gMapContext.map;
29317             options.radius = radius;
29318             options.center = center;
29319             this.gMapContext.circle = new google.maps.Circle(options);
29320             return this.gMapContext.circle;
29321         }
29322         
29323         return null;
29324     },
29325     
29326     setPosition: function(location) 
29327     {
29328         this.gMapContext.location = location;
29329         this.gMapContext.marker.setPosition(location);
29330         this.gMapContext.map.panTo(location);
29331         this.drawCircle(location, this.gMapContext.radius, {});
29332         
29333         var _this = this;
29334         
29335         if (this.gMapContext.settings.enableReverseGeocode) {
29336             this.gMapContext.geodecoder.geocode({
29337                 latLng: this.gMapContext.location
29338             }, function(results, status) {
29339                 
29340                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29341                     _this.gMapContext.locationName = results[0].formatted_address;
29342                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29343                     
29344                     _this.fireEvent('positionchanged', this, location);
29345                 }
29346             });
29347             
29348             return;
29349         }
29350         
29351         this.fireEvent('positionchanged', this, location);
29352     },
29353     
29354     resize: function()
29355     {
29356         google.maps.event.trigger(this.gMapContext.map, "resize");
29357         
29358         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29359         
29360         this.fireEvent('resize', this);
29361     },
29362     
29363     setPositionByLatLng: function(latitude, longitude)
29364     {
29365         this.setPosition(new google.maps.LatLng(latitude, longitude));
29366     },
29367     
29368     getCurrentPosition: function() 
29369     {
29370         return {
29371             latitude: this.gMapContext.location.lat(),
29372             longitude: this.gMapContext.location.lng()
29373         };
29374     },
29375     
29376     getAddressName: function() 
29377     {
29378         return this.gMapContext.locationName;
29379     },
29380     
29381     getAddressComponents: function() 
29382     {
29383         return this.gMapContext.addressComponents;
29384     },
29385     
29386     address_component_from_google_geocode: function(address_components) 
29387     {
29388         var result = {};
29389         
29390         for (var i = 0; i < address_components.length; i++) {
29391             var component = address_components[i];
29392             if (component.types.indexOf("postal_code") >= 0) {
29393                 result.postalCode = component.short_name;
29394             } else if (component.types.indexOf("street_number") >= 0) {
29395                 result.streetNumber = component.short_name;
29396             } else if (component.types.indexOf("route") >= 0) {
29397                 result.streetName = component.short_name;
29398             } else if (component.types.indexOf("neighborhood") >= 0) {
29399                 result.city = component.short_name;
29400             } else if (component.types.indexOf("locality") >= 0) {
29401                 result.city = component.short_name;
29402             } else if (component.types.indexOf("sublocality") >= 0) {
29403                 result.district = component.short_name;
29404             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29405                 result.stateOrProvince = component.short_name;
29406             } else if (component.types.indexOf("country") >= 0) {
29407                 result.country = component.short_name;
29408             }
29409         }
29410         
29411         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29412         result.addressLine2 = "";
29413         return result;
29414     },
29415     
29416     setZoomLevel: function(zoom)
29417     {
29418         this.gMapContext.map.setZoom(zoom);
29419     },
29420     
29421     show: function()
29422     {
29423         if(!this.el){
29424             return;
29425         }
29426         
29427         this.el.show();
29428         
29429         this.resize();
29430         
29431         this.fireEvent('show', this);
29432     },
29433     
29434     hide: function()
29435     {
29436         if(!this.el){
29437             return;
29438         }
29439         
29440         this.el.hide();
29441         
29442         this.fireEvent('hide', this);
29443     }
29444     
29445 });
29446
29447 Roo.apply(Roo.bootstrap.LocationPicker, {
29448     
29449     OverlayView : function(map, options)
29450     {
29451         options = options || {};
29452         
29453         this.setMap(map);
29454     }
29455     
29456     
29457 });/**
29458  * @class Roo.bootstrap.Alert
29459  * @extends Roo.bootstrap.Component
29460  * Bootstrap Alert class - shows an alert area box
29461  * eg
29462  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29463   Enter a valid email address
29464 </div>
29465  * @licence LGPL
29466  * @cfg {String} title The title of alert
29467  * @cfg {String} html The content of alert
29468  * @cfg {String} weight (  success | info | warning | danger )
29469  * @cfg {String} faicon font-awesomeicon
29470  * 
29471  * @constructor
29472  * Create a new alert
29473  * @param {Object} config The config object
29474  */
29475
29476
29477 Roo.bootstrap.Alert = function(config){
29478     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29479     
29480 };
29481
29482 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29483     
29484     title: '',
29485     html: '',
29486     weight: false,
29487     faicon: false,
29488     
29489     getAutoCreate : function()
29490     {
29491         
29492         var cfg = {
29493             tag : 'div',
29494             cls : 'alert',
29495             cn : [
29496                 {
29497                     tag : 'i',
29498                     cls : 'roo-alert-icon'
29499                     
29500                 },
29501                 {
29502                     tag : 'b',
29503                     cls : 'roo-alert-title',
29504                     html : this.title
29505                 },
29506                 {
29507                     tag : 'span',
29508                     cls : 'roo-alert-text',
29509                     html : this.html
29510                 }
29511             ]
29512         };
29513         
29514         if(this.faicon){
29515             cfg.cn[0].cls += ' fa ' + this.faicon;
29516         }
29517         
29518         if(this.weight){
29519             cfg.cls += ' alert-' + this.weight;
29520         }
29521         
29522         return cfg;
29523     },
29524     
29525     initEvents: function() 
29526     {
29527         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29528     },
29529     
29530     setTitle : function(str)
29531     {
29532         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29533     },
29534     
29535     setText : function(str)
29536     {
29537         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29538     },
29539     
29540     setWeight : function(weight)
29541     {
29542         if(this.weight){
29543             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29544         }
29545         
29546         this.weight = weight;
29547         
29548         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29549     },
29550     
29551     setIcon : function(icon)
29552     {
29553         if(this.faicon){
29554             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29555         }
29556         
29557         this.faicon = icon;
29558         
29559         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29560     },
29561     
29562     hide: function() 
29563     {
29564         this.el.hide();   
29565     },
29566     
29567     show: function() 
29568     {  
29569         this.el.show();   
29570     }
29571     
29572 });
29573
29574  
29575 /*
29576 * Licence: LGPL
29577 */
29578
29579 /**
29580  * @class Roo.bootstrap.UploadCropbox
29581  * @extends Roo.bootstrap.Component
29582  * Bootstrap UploadCropbox class
29583  * @cfg {String} emptyText show when image has been loaded
29584  * @cfg {String} rotateNotify show when image too small to rotate
29585  * @cfg {Number} errorTimeout default 3000
29586  * @cfg {Number} minWidth default 300
29587  * @cfg {Number} minHeight default 300
29588  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29589  * @cfg {Boolean} isDocument (true|false) default false
29590  * @cfg {String} url action url
29591  * @cfg {String} paramName default 'imageUpload'
29592  * @cfg {String} method default POST
29593  * @cfg {Boolean} loadMask (true|false) default true
29594  * @cfg {Boolean} loadingText default 'Loading...'
29595  * 
29596  * @constructor
29597  * Create a new UploadCropbox
29598  * @param {Object} config The config object
29599  */
29600
29601 Roo.bootstrap.UploadCropbox = function(config){
29602     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29603     
29604     this.addEvents({
29605         /**
29606          * @event beforeselectfile
29607          * Fire before select file
29608          * @param {Roo.bootstrap.UploadCropbox} this
29609          */
29610         "beforeselectfile" : true,
29611         /**
29612          * @event initial
29613          * Fire after initEvent
29614          * @param {Roo.bootstrap.UploadCropbox} this
29615          */
29616         "initial" : true,
29617         /**
29618          * @event crop
29619          * Fire after initEvent
29620          * @param {Roo.bootstrap.UploadCropbox} this
29621          * @param {String} data
29622          */
29623         "crop" : true,
29624         /**
29625          * @event prepare
29626          * Fire when preparing the file data
29627          * @param {Roo.bootstrap.UploadCropbox} this
29628          * @param {Object} file
29629          */
29630         "prepare" : true,
29631         /**
29632          * @event exception
29633          * Fire when get exception
29634          * @param {Roo.bootstrap.UploadCropbox} this
29635          * @param {XMLHttpRequest} xhr
29636          */
29637         "exception" : true,
29638         /**
29639          * @event beforeloadcanvas
29640          * Fire before load the canvas
29641          * @param {Roo.bootstrap.UploadCropbox} this
29642          * @param {String} src
29643          */
29644         "beforeloadcanvas" : true,
29645         /**
29646          * @event trash
29647          * Fire when trash image
29648          * @param {Roo.bootstrap.UploadCropbox} this
29649          */
29650         "trash" : true,
29651         /**
29652          * @event download
29653          * Fire when download the image
29654          * @param {Roo.bootstrap.UploadCropbox} this
29655          */
29656         "download" : true,
29657         /**
29658          * @event footerbuttonclick
29659          * Fire when footerbuttonclick
29660          * @param {Roo.bootstrap.UploadCropbox} this
29661          * @param {String} type
29662          */
29663         "footerbuttonclick" : true,
29664         /**
29665          * @event resize
29666          * Fire when resize
29667          * @param {Roo.bootstrap.UploadCropbox} this
29668          */
29669         "resize" : true,
29670         /**
29671          * @event rotate
29672          * Fire when rotate the image
29673          * @param {Roo.bootstrap.UploadCropbox} this
29674          * @param {String} pos
29675          */
29676         "rotate" : true,
29677         /**
29678          * @event inspect
29679          * Fire when inspect the file
29680          * @param {Roo.bootstrap.UploadCropbox} this
29681          * @param {Object} file
29682          */
29683         "inspect" : true,
29684         /**
29685          * @event upload
29686          * Fire when xhr upload the file
29687          * @param {Roo.bootstrap.UploadCropbox} this
29688          * @param {Object} data
29689          */
29690         "upload" : true,
29691         /**
29692          * @event arrange
29693          * Fire when arrange the file data
29694          * @param {Roo.bootstrap.UploadCropbox} this
29695          * @param {Object} formData
29696          */
29697         "arrange" : true
29698     });
29699     
29700     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29701 };
29702
29703 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29704     
29705     emptyText : 'Click to upload image',
29706     rotateNotify : 'Image is too small to rotate',
29707     errorTimeout : 3000,
29708     scale : 0,
29709     baseScale : 1,
29710     rotate : 0,
29711     dragable : false,
29712     pinching : false,
29713     mouseX : 0,
29714     mouseY : 0,
29715     cropData : false,
29716     minWidth : 300,
29717     minHeight : 300,
29718     file : false,
29719     exif : {},
29720     baseRotate : 1,
29721     cropType : 'image/jpeg',
29722     buttons : false,
29723     canvasLoaded : false,
29724     isDocument : false,
29725     method : 'POST',
29726     paramName : 'imageUpload',
29727     loadMask : true,
29728     loadingText : 'Loading...',
29729     maskEl : false,
29730     
29731     getAutoCreate : function()
29732     {
29733         var cfg = {
29734             tag : 'div',
29735             cls : 'roo-upload-cropbox',
29736             cn : [
29737                 {
29738                     tag : 'input',
29739                     cls : 'roo-upload-cropbox-selector',
29740                     type : 'file'
29741                 },
29742                 {
29743                     tag : 'div',
29744                     cls : 'roo-upload-cropbox-body',
29745                     style : 'cursor:pointer',
29746                     cn : [
29747                         {
29748                             tag : 'div',
29749                             cls : 'roo-upload-cropbox-preview'
29750                         },
29751                         {
29752                             tag : 'div',
29753                             cls : 'roo-upload-cropbox-thumb'
29754                         },
29755                         {
29756                             tag : 'div',
29757                             cls : 'roo-upload-cropbox-empty-notify',
29758                             html : this.emptyText
29759                         },
29760                         {
29761                             tag : 'div',
29762                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29763                             html : this.rotateNotify
29764                         }
29765                     ]
29766                 },
29767                 {
29768                     tag : 'div',
29769                     cls : 'roo-upload-cropbox-footer',
29770                     cn : {
29771                         tag : 'div',
29772                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29773                         cn : []
29774                     }
29775                 }
29776             ]
29777         };
29778         
29779         return cfg;
29780     },
29781     
29782     onRender : function(ct, position)
29783     {
29784         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29785         
29786         if (this.buttons.length) {
29787             
29788             Roo.each(this.buttons, function(bb) {
29789                 
29790                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29791                 
29792                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29793                 
29794             }, this);
29795         }
29796         
29797         if(this.loadMask){
29798             this.maskEl = this.el;
29799         }
29800     },
29801     
29802     initEvents : function()
29803     {
29804         this.urlAPI = (window.createObjectURL && window) || 
29805                                 (window.URL && URL.revokeObjectURL && URL) || 
29806                                 (window.webkitURL && webkitURL);
29807                         
29808         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29809         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29810         
29811         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29812         this.selectorEl.hide();
29813         
29814         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29815         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29816         
29817         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29818         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29819         this.thumbEl.hide();
29820         
29821         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29822         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29823         
29824         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29825         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29826         this.errorEl.hide();
29827         
29828         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29829         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29830         this.footerEl.hide();
29831         
29832         this.setThumbBoxSize();
29833         
29834         this.bind();
29835         
29836         this.resize();
29837         
29838         this.fireEvent('initial', this);
29839     },
29840
29841     bind : function()
29842     {
29843         var _this = this;
29844         
29845         window.addEventListener("resize", function() { _this.resize(); } );
29846         
29847         this.bodyEl.on('click', this.beforeSelectFile, this);
29848         
29849         if(Roo.isTouch){
29850             this.bodyEl.on('touchstart', this.onTouchStart, this);
29851             this.bodyEl.on('touchmove', this.onTouchMove, this);
29852             this.bodyEl.on('touchend', this.onTouchEnd, this);
29853         }
29854         
29855         if(!Roo.isTouch){
29856             this.bodyEl.on('mousedown', this.onMouseDown, this);
29857             this.bodyEl.on('mousemove', this.onMouseMove, this);
29858             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29859             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29860             Roo.get(document).on('mouseup', this.onMouseUp, this);
29861         }
29862         
29863         this.selectorEl.on('change', this.onFileSelected, this);
29864     },
29865     
29866     reset : function()
29867     {    
29868         this.scale = 0;
29869         this.baseScale = 1;
29870         this.rotate = 0;
29871         this.baseRotate = 1;
29872         this.dragable = false;
29873         this.pinching = false;
29874         this.mouseX = 0;
29875         this.mouseY = 0;
29876         this.cropData = false;
29877         this.notifyEl.dom.innerHTML = this.emptyText;
29878         
29879         this.selectorEl.dom.value = '';
29880         
29881     },
29882     
29883     resize : function()
29884     {
29885         if(this.fireEvent('resize', this) != false){
29886             this.setThumbBoxPosition();
29887             this.setCanvasPosition();
29888         }
29889     },
29890     
29891     onFooterButtonClick : function(e, el, o, type)
29892     {
29893         switch (type) {
29894             case 'rotate-left' :
29895                 this.onRotateLeft(e);
29896                 break;
29897             case 'rotate-right' :
29898                 this.onRotateRight(e);
29899                 break;
29900             case 'picture' :
29901                 this.beforeSelectFile(e);
29902                 break;
29903             case 'trash' :
29904                 this.trash(e);
29905                 break;
29906             case 'crop' :
29907                 this.crop(e);
29908                 break;
29909             case 'download' :
29910                 this.download(e);
29911                 break;
29912             default :
29913                 break;
29914         }
29915         
29916         this.fireEvent('footerbuttonclick', this, type);
29917     },
29918     
29919     beforeSelectFile : function(e)
29920     {
29921         e.preventDefault();
29922         
29923         if(this.fireEvent('beforeselectfile', this) != false){
29924             this.selectorEl.dom.click();
29925         }
29926     },
29927     
29928     onFileSelected : function(e)
29929     {
29930         e.preventDefault();
29931         
29932         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29933             return;
29934         }
29935         
29936         var file = this.selectorEl.dom.files[0];
29937         
29938         if(this.fireEvent('inspect', this, file) != false){
29939             this.prepare(file);
29940         }
29941         
29942     },
29943     
29944     trash : function(e)
29945     {
29946         this.fireEvent('trash', this);
29947     },
29948     
29949     download : function(e)
29950     {
29951         this.fireEvent('download', this);
29952     },
29953     
29954     loadCanvas : function(src)
29955     {   
29956         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29957             
29958             this.reset();
29959             
29960             this.imageEl = document.createElement('img');
29961             
29962             var _this = this;
29963             
29964             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29965             
29966             this.imageEl.src = src;
29967         }
29968     },
29969     
29970     onLoadCanvas : function()
29971     {   
29972         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29973         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29974         
29975         this.bodyEl.un('click', this.beforeSelectFile, this);
29976         
29977         this.notifyEl.hide();
29978         this.thumbEl.show();
29979         this.footerEl.show();
29980         
29981         this.baseRotateLevel();
29982         
29983         if(this.isDocument){
29984             this.setThumbBoxSize();
29985         }
29986         
29987         this.setThumbBoxPosition();
29988         
29989         this.baseScaleLevel();
29990         
29991         this.draw();
29992         
29993         this.resize();
29994         
29995         this.canvasLoaded = true;
29996         
29997         if(this.loadMask){
29998             this.maskEl.unmask();
29999         }
30000         
30001     },
30002     
30003     setCanvasPosition : function()
30004     {   
30005         if(!this.canvasEl){
30006             return;
30007         }
30008         
30009         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30010         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30011         
30012         this.previewEl.setLeft(pw);
30013         this.previewEl.setTop(ph);
30014         
30015     },
30016     
30017     onMouseDown : function(e)
30018     {   
30019         e.stopEvent();
30020         
30021         this.dragable = true;
30022         this.pinching = false;
30023         
30024         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30025             this.dragable = false;
30026             return;
30027         }
30028         
30029         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30030         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30031         
30032     },
30033     
30034     onMouseMove : function(e)
30035     {   
30036         e.stopEvent();
30037         
30038         if(!this.canvasLoaded){
30039             return;
30040         }
30041         
30042         if (!this.dragable){
30043             return;
30044         }
30045         
30046         var minX = Math.ceil(this.thumbEl.getLeft(true));
30047         var minY = Math.ceil(this.thumbEl.getTop(true));
30048         
30049         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30050         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30051         
30052         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30053         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30054         
30055         x = x - this.mouseX;
30056         y = y - this.mouseY;
30057         
30058         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30059         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30060         
30061         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30062         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30063         
30064         this.previewEl.setLeft(bgX);
30065         this.previewEl.setTop(bgY);
30066         
30067         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30068         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30069     },
30070     
30071     onMouseUp : function(e)
30072     {   
30073         e.stopEvent();
30074         
30075         this.dragable = false;
30076     },
30077     
30078     onMouseWheel : function(e)
30079     {   
30080         e.stopEvent();
30081         
30082         this.startScale = this.scale;
30083         
30084         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30085         
30086         if(!this.zoomable()){
30087             this.scale = this.startScale;
30088             return;
30089         }
30090         
30091         this.draw();
30092         
30093         return;
30094     },
30095     
30096     zoomable : function()
30097     {
30098         var minScale = this.thumbEl.getWidth() / this.minWidth;
30099         
30100         if(this.minWidth < this.minHeight){
30101             minScale = this.thumbEl.getHeight() / this.minHeight;
30102         }
30103         
30104         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30105         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30106         
30107         if(
30108                 this.isDocument &&
30109                 (this.rotate == 0 || this.rotate == 180) && 
30110                 (
30111                     width > this.imageEl.OriginWidth || 
30112                     height > this.imageEl.OriginHeight ||
30113                     (width < this.minWidth && height < this.minHeight)
30114                 )
30115         ){
30116             return false;
30117         }
30118         
30119         if(
30120                 this.isDocument &&
30121                 (this.rotate == 90 || this.rotate == 270) && 
30122                 (
30123                     width > this.imageEl.OriginWidth || 
30124                     height > this.imageEl.OriginHeight ||
30125                     (width < this.minHeight && height < this.minWidth)
30126                 )
30127         ){
30128             return false;
30129         }
30130         
30131         if(
30132                 !this.isDocument &&
30133                 (this.rotate == 0 || this.rotate == 180) && 
30134                 (
30135                     width < this.minWidth || 
30136                     width > this.imageEl.OriginWidth || 
30137                     height < this.minHeight || 
30138                     height > this.imageEl.OriginHeight
30139                 )
30140         ){
30141             return false;
30142         }
30143         
30144         if(
30145                 !this.isDocument &&
30146                 (this.rotate == 90 || this.rotate == 270) && 
30147                 (
30148                     width < this.minHeight || 
30149                     width > this.imageEl.OriginWidth || 
30150                     height < this.minWidth || 
30151                     height > this.imageEl.OriginHeight
30152                 )
30153         ){
30154             return false;
30155         }
30156         
30157         return true;
30158         
30159     },
30160     
30161     onRotateLeft : function(e)
30162     {   
30163         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30164             
30165             var minScale = this.thumbEl.getWidth() / this.minWidth;
30166             
30167             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30168             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30169             
30170             this.startScale = this.scale;
30171             
30172             while (this.getScaleLevel() < minScale){
30173             
30174                 this.scale = this.scale + 1;
30175                 
30176                 if(!this.zoomable()){
30177                     break;
30178                 }
30179                 
30180                 if(
30181                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30182                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30183                 ){
30184                     continue;
30185                 }
30186                 
30187                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30188
30189                 this.draw();
30190                 
30191                 return;
30192             }
30193             
30194             this.scale = this.startScale;
30195             
30196             this.onRotateFail();
30197             
30198             return false;
30199         }
30200         
30201         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30202
30203         if(this.isDocument){
30204             this.setThumbBoxSize();
30205             this.setThumbBoxPosition();
30206             this.setCanvasPosition();
30207         }
30208         
30209         this.draw();
30210         
30211         this.fireEvent('rotate', this, 'left');
30212         
30213     },
30214     
30215     onRotateRight : function(e)
30216     {
30217         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30218             
30219             var minScale = this.thumbEl.getWidth() / this.minWidth;
30220         
30221             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30222             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30223             
30224             this.startScale = this.scale;
30225             
30226             while (this.getScaleLevel() < minScale){
30227             
30228                 this.scale = this.scale + 1;
30229                 
30230                 if(!this.zoomable()){
30231                     break;
30232                 }
30233                 
30234                 if(
30235                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30236                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30237                 ){
30238                     continue;
30239                 }
30240                 
30241                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30242
30243                 this.draw();
30244                 
30245                 return;
30246             }
30247             
30248             this.scale = this.startScale;
30249             
30250             this.onRotateFail();
30251             
30252             return false;
30253         }
30254         
30255         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30256
30257         if(this.isDocument){
30258             this.setThumbBoxSize();
30259             this.setThumbBoxPosition();
30260             this.setCanvasPosition();
30261         }
30262         
30263         this.draw();
30264         
30265         this.fireEvent('rotate', this, 'right');
30266     },
30267     
30268     onRotateFail : function()
30269     {
30270         this.errorEl.show(true);
30271         
30272         var _this = this;
30273         
30274         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30275     },
30276     
30277     draw : function()
30278     {
30279         this.previewEl.dom.innerHTML = '';
30280         
30281         var canvasEl = document.createElement("canvas");
30282         
30283         var contextEl = canvasEl.getContext("2d");
30284         
30285         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30286         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30287         var center = this.imageEl.OriginWidth / 2;
30288         
30289         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30290             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30291             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30292             center = this.imageEl.OriginHeight / 2;
30293         }
30294         
30295         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30296         
30297         contextEl.translate(center, center);
30298         contextEl.rotate(this.rotate * Math.PI / 180);
30299
30300         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30301         
30302         this.canvasEl = document.createElement("canvas");
30303         
30304         this.contextEl = this.canvasEl.getContext("2d");
30305         
30306         switch (this.rotate) {
30307             case 0 :
30308                 
30309                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30310                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30311                 
30312                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30313                 
30314                 break;
30315             case 90 : 
30316                 
30317                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30318                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30319                 
30320                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30321                     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);
30322                     break;
30323                 }
30324                 
30325                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30326                 
30327                 break;
30328             case 180 :
30329                 
30330                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30331                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30332                 
30333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30334                     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);
30335                     break;
30336                 }
30337                 
30338                 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);
30339                 
30340                 break;
30341             case 270 :
30342                 
30343                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30344                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30345         
30346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30347                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30348                     break;
30349                 }
30350                 
30351                 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);
30352                 
30353                 break;
30354             default : 
30355                 break;
30356         }
30357         
30358         this.previewEl.appendChild(this.canvasEl);
30359         
30360         this.setCanvasPosition();
30361     },
30362     
30363     crop : function()
30364     {
30365         if(!this.canvasLoaded){
30366             return;
30367         }
30368         
30369         var imageCanvas = document.createElement("canvas");
30370         
30371         var imageContext = imageCanvas.getContext("2d");
30372         
30373         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30374         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30375         
30376         var center = imageCanvas.width / 2;
30377         
30378         imageContext.translate(center, center);
30379         
30380         imageContext.rotate(this.rotate * Math.PI / 180);
30381         
30382         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30383         
30384         var canvas = document.createElement("canvas");
30385         
30386         var context = canvas.getContext("2d");
30387                 
30388         canvas.width = this.minWidth;
30389         canvas.height = this.minHeight;
30390
30391         switch (this.rotate) {
30392             case 0 :
30393                 
30394                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30395                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30396                 
30397                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30398                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30399                 
30400                 var targetWidth = this.minWidth - 2 * x;
30401                 var targetHeight = this.minHeight - 2 * y;
30402                 
30403                 var scale = 1;
30404                 
30405                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30406                     scale = targetWidth / width;
30407                 }
30408                 
30409                 if(x > 0 && y == 0){
30410                     scale = targetHeight / height;
30411                 }
30412                 
30413                 if(x > 0 && y > 0){
30414                     scale = targetWidth / width;
30415                     
30416                     if(width < height){
30417                         scale = targetHeight / height;
30418                     }
30419                 }
30420                 
30421                 context.scale(scale, scale);
30422                 
30423                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30424                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30425
30426                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30427                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30428
30429                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30430                 
30431                 break;
30432             case 90 : 
30433                 
30434                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30435                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30436                 
30437                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30438                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30439                 
30440                 var targetWidth = this.minWidth - 2 * x;
30441                 var targetHeight = this.minHeight - 2 * y;
30442                 
30443                 var scale = 1;
30444                 
30445                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30446                     scale = targetWidth / width;
30447                 }
30448                 
30449                 if(x > 0 && y == 0){
30450                     scale = targetHeight / height;
30451                 }
30452                 
30453                 if(x > 0 && y > 0){
30454                     scale = targetWidth / width;
30455                     
30456                     if(width < height){
30457                         scale = targetHeight / height;
30458                     }
30459                 }
30460                 
30461                 context.scale(scale, scale);
30462                 
30463                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30464                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30465
30466                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30467                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30468                 
30469                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30470                 
30471                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30472                 
30473                 break;
30474             case 180 :
30475                 
30476                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30477                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30478                 
30479                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30480                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30481                 
30482                 var targetWidth = this.minWidth - 2 * x;
30483                 var targetHeight = this.minHeight - 2 * y;
30484                 
30485                 var scale = 1;
30486                 
30487                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30488                     scale = targetWidth / width;
30489                 }
30490                 
30491                 if(x > 0 && y == 0){
30492                     scale = targetHeight / height;
30493                 }
30494                 
30495                 if(x > 0 && y > 0){
30496                     scale = targetWidth / width;
30497                     
30498                     if(width < height){
30499                         scale = targetHeight / height;
30500                     }
30501                 }
30502                 
30503                 context.scale(scale, scale);
30504                 
30505                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30506                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30507
30508                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30509                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30510
30511                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30512                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30513                 
30514                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30515                 
30516                 break;
30517             case 270 :
30518                 
30519                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30520                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30521                 
30522                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30523                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30524                 
30525                 var targetWidth = this.minWidth - 2 * x;
30526                 var targetHeight = this.minHeight - 2 * y;
30527                 
30528                 var scale = 1;
30529                 
30530                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30531                     scale = targetWidth / width;
30532                 }
30533                 
30534                 if(x > 0 && y == 0){
30535                     scale = targetHeight / height;
30536                 }
30537                 
30538                 if(x > 0 && y > 0){
30539                     scale = targetWidth / width;
30540                     
30541                     if(width < height){
30542                         scale = targetHeight / height;
30543                     }
30544                 }
30545                 
30546                 context.scale(scale, scale);
30547                 
30548                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30549                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30550
30551                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30552                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30553                 
30554                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30555                 
30556                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30557                 
30558                 break;
30559             default : 
30560                 break;
30561         }
30562         
30563         this.cropData = canvas.toDataURL(this.cropType);
30564         
30565         if(this.fireEvent('crop', this, this.cropData) !== false){
30566             this.process(this.file, this.cropData);
30567         }
30568         
30569         return;
30570         
30571     },
30572     
30573     setThumbBoxSize : function()
30574     {
30575         var width, height;
30576         
30577         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30578             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30579             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30580             
30581             this.minWidth = width;
30582             this.minHeight = height;
30583             
30584             if(this.rotate == 90 || this.rotate == 270){
30585                 this.minWidth = height;
30586                 this.minHeight = width;
30587             }
30588         }
30589         
30590         height = 300;
30591         width = Math.ceil(this.minWidth * height / this.minHeight);
30592         
30593         if(this.minWidth > this.minHeight){
30594             width = 300;
30595             height = Math.ceil(this.minHeight * width / this.minWidth);
30596         }
30597         
30598         this.thumbEl.setStyle({
30599             width : width + 'px',
30600             height : height + 'px'
30601         });
30602
30603         return;
30604             
30605     },
30606     
30607     setThumbBoxPosition : function()
30608     {
30609         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30610         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30611         
30612         this.thumbEl.setLeft(x);
30613         this.thumbEl.setTop(y);
30614         
30615     },
30616     
30617     baseRotateLevel : function()
30618     {
30619         this.baseRotate = 1;
30620         
30621         if(
30622                 typeof(this.exif) != 'undefined' &&
30623                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30624                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30625         ){
30626             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30627         }
30628         
30629         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30630         
30631     },
30632     
30633     baseScaleLevel : function()
30634     {
30635         var width, height;
30636         
30637         if(this.isDocument){
30638             
30639             if(this.baseRotate == 6 || this.baseRotate == 8){
30640             
30641                 height = this.thumbEl.getHeight();
30642                 this.baseScale = height / this.imageEl.OriginWidth;
30643
30644                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30645                     width = this.thumbEl.getWidth();
30646                     this.baseScale = width / this.imageEl.OriginHeight;
30647                 }
30648
30649                 return;
30650             }
30651
30652             height = this.thumbEl.getHeight();
30653             this.baseScale = height / this.imageEl.OriginHeight;
30654
30655             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30656                 width = this.thumbEl.getWidth();
30657                 this.baseScale = width / this.imageEl.OriginWidth;
30658             }
30659
30660             return;
30661         }
30662         
30663         if(this.baseRotate == 6 || this.baseRotate == 8){
30664             
30665             width = this.thumbEl.getHeight();
30666             this.baseScale = width / this.imageEl.OriginHeight;
30667             
30668             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30669                 height = this.thumbEl.getWidth();
30670                 this.baseScale = height / this.imageEl.OriginHeight;
30671             }
30672             
30673             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30674                 height = this.thumbEl.getWidth();
30675                 this.baseScale = height / this.imageEl.OriginHeight;
30676                 
30677                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30678                     width = this.thumbEl.getHeight();
30679                     this.baseScale = width / this.imageEl.OriginWidth;
30680                 }
30681             }
30682             
30683             return;
30684         }
30685         
30686         width = this.thumbEl.getWidth();
30687         this.baseScale = width / this.imageEl.OriginWidth;
30688         
30689         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30690             height = this.thumbEl.getHeight();
30691             this.baseScale = height / this.imageEl.OriginHeight;
30692         }
30693         
30694         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30695             
30696             height = this.thumbEl.getHeight();
30697             this.baseScale = height / this.imageEl.OriginHeight;
30698             
30699             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30700                 width = this.thumbEl.getWidth();
30701                 this.baseScale = width / this.imageEl.OriginWidth;
30702             }
30703             
30704         }
30705         
30706         return;
30707     },
30708     
30709     getScaleLevel : function()
30710     {
30711         return this.baseScale * Math.pow(1.1, this.scale);
30712     },
30713     
30714     onTouchStart : function(e)
30715     {
30716         if(!this.canvasLoaded){
30717             this.beforeSelectFile(e);
30718             return;
30719         }
30720         
30721         var touches = e.browserEvent.touches;
30722         
30723         if(!touches){
30724             return;
30725         }
30726         
30727         if(touches.length == 1){
30728             this.onMouseDown(e);
30729             return;
30730         }
30731         
30732         if(touches.length != 2){
30733             return;
30734         }
30735         
30736         var coords = [];
30737         
30738         for(var i = 0, finger; finger = touches[i]; i++){
30739             coords.push(finger.pageX, finger.pageY);
30740         }
30741         
30742         var x = Math.pow(coords[0] - coords[2], 2);
30743         var y = Math.pow(coords[1] - coords[3], 2);
30744         
30745         this.startDistance = Math.sqrt(x + y);
30746         
30747         this.startScale = this.scale;
30748         
30749         this.pinching = true;
30750         this.dragable = false;
30751         
30752     },
30753     
30754     onTouchMove : function(e)
30755     {
30756         if(!this.pinching && !this.dragable){
30757             return;
30758         }
30759         
30760         var touches = e.browserEvent.touches;
30761         
30762         if(!touches){
30763             return;
30764         }
30765         
30766         if(this.dragable){
30767             this.onMouseMove(e);
30768             return;
30769         }
30770         
30771         var coords = [];
30772         
30773         for(var i = 0, finger; finger = touches[i]; i++){
30774             coords.push(finger.pageX, finger.pageY);
30775         }
30776         
30777         var x = Math.pow(coords[0] - coords[2], 2);
30778         var y = Math.pow(coords[1] - coords[3], 2);
30779         
30780         this.endDistance = Math.sqrt(x + y);
30781         
30782         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30783         
30784         if(!this.zoomable()){
30785             this.scale = this.startScale;
30786             return;
30787         }
30788         
30789         this.draw();
30790         
30791     },
30792     
30793     onTouchEnd : function(e)
30794     {
30795         this.pinching = false;
30796         this.dragable = false;
30797         
30798     },
30799     
30800     process : function(file, crop)
30801     {
30802         if(this.loadMask){
30803             this.maskEl.mask(this.loadingText);
30804         }
30805         
30806         this.xhr = new XMLHttpRequest();
30807         
30808         file.xhr = this.xhr;
30809
30810         this.xhr.open(this.method, this.url, true);
30811         
30812         var headers = {
30813             "Accept": "application/json",
30814             "Cache-Control": "no-cache",
30815             "X-Requested-With": "XMLHttpRequest"
30816         };
30817         
30818         for (var headerName in headers) {
30819             var headerValue = headers[headerName];
30820             if (headerValue) {
30821                 this.xhr.setRequestHeader(headerName, headerValue);
30822             }
30823         }
30824         
30825         var _this = this;
30826         
30827         this.xhr.onload = function()
30828         {
30829             _this.xhrOnLoad(_this.xhr);
30830         }
30831         
30832         this.xhr.onerror = function()
30833         {
30834             _this.xhrOnError(_this.xhr);
30835         }
30836         
30837         var formData = new FormData();
30838
30839         formData.append('returnHTML', 'NO');
30840         
30841         if(crop){
30842             formData.append('crop', crop);
30843         }
30844         
30845         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30846             formData.append(this.paramName, file, file.name);
30847         }
30848         
30849         if(typeof(file.filename) != 'undefined'){
30850             formData.append('filename', file.filename);
30851         }
30852         
30853         if(typeof(file.mimetype) != 'undefined'){
30854             formData.append('mimetype', file.mimetype);
30855         }
30856         
30857         if(this.fireEvent('arrange', this, formData) != false){
30858             this.xhr.send(formData);
30859         };
30860     },
30861     
30862     xhrOnLoad : function(xhr)
30863     {
30864         if(this.loadMask){
30865             this.maskEl.unmask();
30866         }
30867         
30868         if (xhr.readyState !== 4) {
30869             this.fireEvent('exception', this, xhr);
30870             return;
30871         }
30872
30873         var response = Roo.decode(xhr.responseText);
30874         
30875         if(!response.success){
30876             this.fireEvent('exception', this, xhr);
30877             return;
30878         }
30879         
30880         var response = Roo.decode(xhr.responseText);
30881         
30882         this.fireEvent('upload', this, response);
30883         
30884     },
30885     
30886     xhrOnError : function()
30887     {
30888         if(this.loadMask){
30889             this.maskEl.unmask();
30890         }
30891         
30892         Roo.log('xhr on error');
30893         
30894         var response = Roo.decode(xhr.responseText);
30895           
30896         Roo.log(response);
30897         
30898     },
30899     
30900     prepare : function(file)
30901     {   
30902         if(this.loadMask){
30903             this.maskEl.mask(this.loadingText);
30904         }
30905         
30906         this.file = false;
30907         this.exif = {};
30908         
30909         if(typeof(file) === 'string'){
30910             this.loadCanvas(file);
30911             return;
30912         }
30913         
30914         if(!file || !this.urlAPI){
30915             return;
30916         }
30917         
30918         this.file = file;
30919         this.cropType = file.type;
30920         
30921         var _this = this;
30922         
30923         if(this.fireEvent('prepare', this, this.file) != false){
30924             
30925             var reader = new FileReader();
30926             
30927             reader.onload = function (e) {
30928                 if (e.target.error) {
30929                     Roo.log(e.target.error);
30930                     return;
30931                 }
30932                 
30933                 var buffer = e.target.result,
30934                     dataView = new DataView(buffer),
30935                     offset = 2,
30936                     maxOffset = dataView.byteLength - 4,
30937                     markerBytes,
30938                     markerLength;
30939                 
30940                 if (dataView.getUint16(0) === 0xffd8) {
30941                     while (offset < maxOffset) {
30942                         markerBytes = dataView.getUint16(offset);
30943                         
30944                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30945                             markerLength = dataView.getUint16(offset + 2) + 2;
30946                             if (offset + markerLength > dataView.byteLength) {
30947                                 Roo.log('Invalid meta data: Invalid segment size.');
30948                                 break;
30949                             }
30950                             
30951                             if(markerBytes == 0xffe1){
30952                                 _this.parseExifData(
30953                                     dataView,
30954                                     offset,
30955                                     markerLength
30956                                 );
30957                             }
30958                             
30959                             offset += markerLength;
30960                             
30961                             continue;
30962                         }
30963                         
30964                         break;
30965                     }
30966                     
30967                 }
30968                 
30969                 var url = _this.urlAPI.createObjectURL(_this.file);
30970                 
30971                 _this.loadCanvas(url);
30972                 
30973                 return;
30974             }
30975             
30976             reader.readAsArrayBuffer(this.file);
30977             
30978         }
30979         
30980     },
30981     
30982     parseExifData : function(dataView, offset, length)
30983     {
30984         var tiffOffset = offset + 10,
30985             littleEndian,
30986             dirOffset;
30987     
30988         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30989             // No Exif data, might be XMP data instead
30990             return;
30991         }
30992         
30993         // Check for the ASCII code for "Exif" (0x45786966):
30994         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30995             // No Exif data, might be XMP data instead
30996             return;
30997         }
30998         if (tiffOffset + 8 > dataView.byteLength) {
30999             Roo.log('Invalid Exif data: Invalid segment size.');
31000             return;
31001         }
31002         // Check for the two null bytes:
31003         if (dataView.getUint16(offset + 8) !== 0x0000) {
31004             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31005             return;
31006         }
31007         // Check the byte alignment:
31008         switch (dataView.getUint16(tiffOffset)) {
31009         case 0x4949:
31010             littleEndian = true;
31011             break;
31012         case 0x4D4D:
31013             littleEndian = false;
31014             break;
31015         default:
31016             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31017             return;
31018         }
31019         // Check for the TIFF tag marker (0x002A):
31020         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31021             Roo.log('Invalid Exif data: Missing TIFF marker.');
31022             return;
31023         }
31024         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31025         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31026         
31027         this.parseExifTags(
31028             dataView,
31029             tiffOffset,
31030             tiffOffset + dirOffset,
31031             littleEndian
31032         );
31033     },
31034     
31035     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31036     {
31037         var tagsNumber,
31038             dirEndOffset,
31039             i;
31040         if (dirOffset + 6 > dataView.byteLength) {
31041             Roo.log('Invalid Exif data: Invalid directory offset.');
31042             return;
31043         }
31044         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31045         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31046         if (dirEndOffset + 4 > dataView.byteLength) {
31047             Roo.log('Invalid Exif data: Invalid directory size.');
31048             return;
31049         }
31050         for (i = 0; i < tagsNumber; i += 1) {
31051             this.parseExifTag(
31052                 dataView,
31053                 tiffOffset,
31054                 dirOffset + 2 + 12 * i, // tag offset
31055                 littleEndian
31056             );
31057         }
31058         // Return the offset to the next directory:
31059         return dataView.getUint32(dirEndOffset, littleEndian);
31060     },
31061     
31062     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31063     {
31064         var tag = dataView.getUint16(offset, littleEndian);
31065         
31066         this.exif[tag] = this.getExifValue(
31067             dataView,
31068             tiffOffset,
31069             offset,
31070             dataView.getUint16(offset + 2, littleEndian), // tag type
31071             dataView.getUint32(offset + 4, littleEndian), // tag length
31072             littleEndian
31073         );
31074     },
31075     
31076     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31077     {
31078         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31079             tagSize,
31080             dataOffset,
31081             values,
31082             i,
31083             str,
31084             c;
31085     
31086         if (!tagType) {
31087             Roo.log('Invalid Exif data: Invalid tag type.');
31088             return;
31089         }
31090         
31091         tagSize = tagType.size * length;
31092         // Determine if the value is contained in the dataOffset bytes,
31093         // or if the value at the dataOffset is a pointer to the actual data:
31094         dataOffset = tagSize > 4 ?
31095                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31096         if (dataOffset + tagSize > dataView.byteLength) {
31097             Roo.log('Invalid Exif data: Invalid data offset.');
31098             return;
31099         }
31100         if (length === 1) {
31101             return tagType.getValue(dataView, dataOffset, littleEndian);
31102         }
31103         values = [];
31104         for (i = 0; i < length; i += 1) {
31105             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31106         }
31107         
31108         if (tagType.ascii) {
31109             str = '';
31110             // Concatenate the chars:
31111             for (i = 0; i < values.length; i += 1) {
31112                 c = values[i];
31113                 // Ignore the terminating NULL byte(s):
31114                 if (c === '\u0000') {
31115                     break;
31116                 }
31117                 str += c;
31118             }
31119             return str;
31120         }
31121         return values;
31122     }
31123     
31124 });
31125
31126 Roo.apply(Roo.bootstrap.UploadCropbox, {
31127     tags : {
31128         'Orientation': 0x0112
31129     },
31130     
31131     Orientation: {
31132             1: 0, //'top-left',
31133 //            2: 'top-right',
31134             3: 180, //'bottom-right',
31135 //            4: 'bottom-left',
31136 //            5: 'left-top',
31137             6: 90, //'right-top',
31138 //            7: 'right-bottom',
31139             8: 270 //'left-bottom'
31140     },
31141     
31142     exifTagTypes : {
31143         // byte, 8-bit unsigned int:
31144         1: {
31145             getValue: function (dataView, dataOffset) {
31146                 return dataView.getUint8(dataOffset);
31147             },
31148             size: 1
31149         },
31150         // ascii, 8-bit byte:
31151         2: {
31152             getValue: function (dataView, dataOffset) {
31153                 return String.fromCharCode(dataView.getUint8(dataOffset));
31154             },
31155             size: 1,
31156             ascii: true
31157         },
31158         // short, 16 bit int:
31159         3: {
31160             getValue: function (dataView, dataOffset, littleEndian) {
31161                 return dataView.getUint16(dataOffset, littleEndian);
31162             },
31163             size: 2
31164         },
31165         // long, 32 bit int:
31166         4: {
31167             getValue: function (dataView, dataOffset, littleEndian) {
31168                 return dataView.getUint32(dataOffset, littleEndian);
31169             },
31170             size: 4
31171         },
31172         // rational = two long values, first is numerator, second is denominator:
31173         5: {
31174             getValue: function (dataView, dataOffset, littleEndian) {
31175                 return dataView.getUint32(dataOffset, littleEndian) /
31176                     dataView.getUint32(dataOffset + 4, littleEndian);
31177             },
31178             size: 8
31179         },
31180         // slong, 32 bit signed int:
31181         9: {
31182             getValue: function (dataView, dataOffset, littleEndian) {
31183                 return dataView.getInt32(dataOffset, littleEndian);
31184             },
31185             size: 4
31186         },
31187         // srational, two slongs, first is numerator, second is denominator:
31188         10: {
31189             getValue: function (dataView, dataOffset, littleEndian) {
31190                 return dataView.getInt32(dataOffset, littleEndian) /
31191                     dataView.getInt32(dataOffset + 4, littleEndian);
31192             },
31193             size: 8
31194         }
31195     },
31196     
31197     footer : {
31198         STANDARD : [
31199             {
31200                 tag : 'div',
31201                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31202                 action : 'rotate-left',
31203                 cn : [
31204                     {
31205                         tag : 'button',
31206                         cls : 'btn btn-default',
31207                         html : '<i class="fa fa-undo"></i>'
31208                     }
31209                 ]
31210             },
31211             {
31212                 tag : 'div',
31213                 cls : 'btn-group roo-upload-cropbox-picture',
31214                 action : 'picture',
31215                 cn : [
31216                     {
31217                         tag : 'button',
31218                         cls : 'btn btn-default',
31219                         html : '<i class="fa fa-picture-o"></i>'
31220                     }
31221                 ]
31222             },
31223             {
31224                 tag : 'div',
31225                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31226                 action : 'rotate-right',
31227                 cn : [
31228                     {
31229                         tag : 'button',
31230                         cls : 'btn btn-default',
31231                         html : '<i class="fa fa-repeat"></i>'
31232                     }
31233                 ]
31234             }
31235         ],
31236         DOCUMENT : [
31237             {
31238                 tag : 'div',
31239                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31240                 action : 'rotate-left',
31241                 cn : [
31242                     {
31243                         tag : 'button',
31244                         cls : 'btn btn-default',
31245                         html : '<i class="fa fa-undo"></i>'
31246                     }
31247                 ]
31248             },
31249             {
31250                 tag : 'div',
31251                 cls : 'btn-group roo-upload-cropbox-download',
31252                 action : 'download',
31253                 cn : [
31254                     {
31255                         tag : 'button',
31256                         cls : 'btn btn-default',
31257                         html : '<i class="fa fa-download"></i>'
31258                     }
31259                 ]
31260             },
31261             {
31262                 tag : 'div',
31263                 cls : 'btn-group roo-upload-cropbox-crop',
31264                 action : 'crop',
31265                 cn : [
31266                     {
31267                         tag : 'button',
31268                         cls : 'btn btn-default',
31269                         html : '<i class="fa fa-crop"></i>'
31270                     }
31271                 ]
31272             },
31273             {
31274                 tag : 'div',
31275                 cls : 'btn-group roo-upload-cropbox-trash',
31276                 action : 'trash',
31277                 cn : [
31278                     {
31279                         tag : 'button',
31280                         cls : 'btn btn-default',
31281                         html : '<i class="fa fa-trash"></i>'
31282                     }
31283                 ]
31284             },
31285             {
31286                 tag : 'div',
31287                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31288                 action : 'rotate-right',
31289                 cn : [
31290                     {
31291                         tag : 'button',
31292                         cls : 'btn btn-default',
31293                         html : '<i class="fa fa-repeat"></i>'
31294                     }
31295                 ]
31296             }
31297         ],
31298         ROTATOR : [
31299             {
31300                 tag : 'div',
31301                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31302                 action : 'rotate-left',
31303                 cn : [
31304                     {
31305                         tag : 'button',
31306                         cls : 'btn btn-default',
31307                         html : '<i class="fa fa-undo"></i>'
31308                     }
31309                 ]
31310             },
31311             {
31312                 tag : 'div',
31313                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31314                 action : 'rotate-right',
31315                 cn : [
31316                     {
31317                         tag : 'button',
31318                         cls : 'btn btn-default',
31319                         html : '<i class="fa fa-repeat"></i>'
31320                     }
31321                 ]
31322             }
31323         ]
31324     }
31325 });
31326
31327 /*
31328 * Licence: LGPL
31329 */
31330
31331 /**
31332  * @class Roo.bootstrap.DocumentManager
31333  * @extends Roo.bootstrap.Component
31334  * Bootstrap DocumentManager class
31335  * @cfg {String} paramName default 'imageUpload'
31336  * @cfg {String} toolTipName default 'filename'
31337  * @cfg {String} method default POST
31338  * @cfg {String} url action url
31339  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31340  * @cfg {Boolean} multiple multiple upload default true
31341  * @cfg {Number} thumbSize default 300
31342  * @cfg {String} fieldLabel
31343  * @cfg {Number} labelWidth default 4
31344  * @cfg {String} labelAlign (left|top) default left
31345  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31346 * @cfg {Number} labellg set the width of label (1-12)
31347  * @cfg {Number} labelmd set the width of label (1-12)
31348  * @cfg {Number} labelsm set the width of label (1-12)
31349  * @cfg {Number} labelxs set the width of label (1-12)
31350  * 
31351  * @constructor
31352  * Create a new DocumentManager
31353  * @param {Object} config The config object
31354  */
31355
31356 Roo.bootstrap.DocumentManager = function(config){
31357     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31358     
31359     this.files = [];
31360     this.delegates = [];
31361     
31362     this.addEvents({
31363         /**
31364          * @event initial
31365          * Fire when initial the DocumentManager
31366          * @param {Roo.bootstrap.DocumentManager} this
31367          */
31368         "initial" : true,
31369         /**
31370          * @event inspect
31371          * inspect selected file
31372          * @param {Roo.bootstrap.DocumentManager} this
31373          * @param {File} file
31374          */
31375         "inspect" : true,
31376         /**
31377          * @event exception
31378          * Fire when xhr load exception
31379          * @param {Roo.bootstrap.DocumentManager} this
31380          * @param {XMLHttpRequest} xhr
31381          */
31382         "exception" : true,
31383         /**
31384          * @event afterupload
31385          * Fire when xhr load exception
31386          * @param {Roo.bootstrap.DocumentManager} this
31387          * @param {XMLHttpRequest} xhr
31388          */
31389         "afterupload" : true,
31390         /**
31391          * @event prepare
31392          * prepare the form data
31393          * @param {Roo.bootstrap.DocumentManager} this
31394          * @param {Object} formData
31395          */
31396         "prepare" : true,
31397         /**
31398          * @event remove
31399          * Fire when remove the file
31400          * @param {Roo.bootstrap.DocumentManager} this
31401          * @param {Object} file
31402          */
31403         "remove" : true,
31404         /**
31405          * @event refresh
31406          * Fire after refresh the file
31407          * @param {Roo.bootstrap.DocumentManager} this
31408          */
31409         "refresh" : true,
31410         /**
31411          * @event click
31412          * Fire after click the image
31413          * @param {Roo.bootstrap.DocumentManager} this
31414          * @param {Object} file
31415          */
31416         "click" : true,
31417         /**
31418          * @event edit
31419          * Fire when upload a image and editable set to true
31420          * @param {Roo.bootstrap.DocumentManager} this
31421          * @param {Object} file
31422          */
31423         "edit" : true,
31424         /**
31425          * @event beforeselectfile
31426          * Fire before select file
31427          * @param {Roo.bootstrap.DocumentManager} this
31428          */
31429         "beforeselectfile" : true,
31430         /**
31431          * @event process
31432          * Fire before process file
31433          * @param {Roo.bootstrap.DocumentManager} this
31434          * @param {Object} file
31435          */
31436         "process" : true,
31437         /**
31438          * @event previewrendered
31439          * Fire when preview rendered
31440          * @param {Roo.bootstrap.DocumentManager} this
31441          * @param {Object} file
31442          */
31443         "previewrendered" : true,
31444         /**
31445          */
31446         "previewResize" : true
31447         
31448     });
31449 };
31450
31451 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31452     
31453     boxes : 0,
31454     inputName : '',
31455     thumbSize : 300,
31456     multiple : true,
31457     files : false,
31458     method : 'POST',
31459     url : '',
31460     paramName : 'imageUpload',
31461     toolTipName : 'filename',
31462     fieldLabel : '',
31463     labelWidth : 4,
31464     labelAlign : 'left',
31465     editable : true,
31466     delegates : false,
31467     xhr : false, 
31468     
31469     labellg : 0,
31470     labelmd : 0,
31471     labelsm : 0,
31472     labelxs : 0,
31473     
31474     getAutoCreate : function()
31475     {   
31476         var managerWidget = {
31477             tag : 'div',
31478             cls : 'roo-document-manager',
31479             cn : [
31480                 {
31481                     tag : 'input',
31482                     cls : 'roo-document-manager-selector',
31483                     type : 'file'
31484                 },
31485                 {
31486                     tag : 'div',
31487                     cls : 'roo-document-manager-uploader',
31488                     cn : [
31489                         {
31490                             tag : 'div',
31491                             cls : 'roo-document-manager-upload-btn',
31492                             html : '<i class="fa fa-plus"></i>'
31493                         }
31494                     ]
31495                     
31496                 }
31497             ]
31498         };
31499         
31500         var content = [
31501             {
31502                 tag : 'div',
31503                 cls : 'column col-md-12',
31504                 cn : managerWidget
31505             }
31506         ];
31507         
31508         if(this.fieldLabel.length){
31509             
31510             content = [
31511                 {
31512                     tag : 'div',
31513                     cls : 'column col-md-12',
31514                     html : this.fieldLabel
31515                 },
31516                 {
31517                     tag : 'div',
31518                     cls : 'column col-md-12',
31519                     cn : managerWidget
31520                 }
31521             ];
31522
31523             if(this.labelAlign == 'left'){
31524                 content = [
31525                     {
31526                         tag : 'div',
31527                         cls : 'column',
31528                         html : this.fieldLabel
31529                     },
31530                     {
31531                         tag : 'div',
31532                         cls : 'column',
31533                         cn : managerWidget
31534                     }
31535                 ];
31536                 
31537                 if(this.labelWidth > 12){
31538                     content[0].style = "width: " + this.labelWidth + 'px';
31539                 }
31540
31541                 if(this.labelWidth < 13 && this.labelmd == 0){
31542                     this.labelmd = this.labelWidth;
31543                 }
31544
31545                 if(this.labellg > 0){
31546                     content[0].cls += ' col-lg-' + this.labellg;
31547                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31548                 }
31549
31550                 if(this.labelmd > 0){
31551                     content[0].cls += ' col-md-' + this.labelmd;
31552                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31553                 }
31554
31555                 if(this.labelsm > 0){
31556                     content[0].cls += ' col-sm-' + this.labelsm;
31557                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31558                 }
31559
31560                 if(this.labelxs > 0){
31561                     content[0].cls += ' col-xs-' + this.labelxs;
31562                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31563                 }
31564                 
31565             }
31566         }
31567         
31568         var cfg = {
31569             tag : 'div',
31570             cls : 'row clearfix',
31571             cn : content
31572         };
31573         
31574         return cfg;
31575         
31576     },
31577     
31578     initEvents : function()
31579     {
31580         this.managerEl = this.el.select('.roo-document-manager', true).first();
31581         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31582         
31583         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31584         this.selectorEl.hide();
31585         
31586         if(this.multiple){
31587             this.selectorEl.attr('multiple', 'multiple');
31588         }
31589         
31590         this.selectorEl.on('change', this.onFileSelected, this);
31591         
31592         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31593         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31594         
31595         this.uploader.on('click', this.onUploaderClick, this);
31596         
31597         this.renderProgressDialog();
31598         
31599         var _this = this;
31600         
31601         window.addEventListener("resize", function() { _this.refresh(); } );
31602         
31603         this.fireEvent('initial', this);
31604     },
31605     
31606     renderProgressDialog : function()
31607     {
31608         var _this = this;
31609         
31610         this.progressDialog = new Roo.bootstrap.Modal({
31611             cls : 'roo-document-manager-progress-dialog',
31612             allow_close : false,
31613             animate : false,
31614             title : '',
31615             buttons : [
31616                 {
31617                     name  :'cancel',
31618                     weight : 'danger',
31619                     html : 'Cancel'
31620                 }
31621             ], 
31622             listeners : { 
31623                 btnclick : function() {
31624                     _this.uploadCancel();
31625                     this.hide();
31626                 }
31627             }
31628         });
31629          
31630         this.progressDialog.render(Roo.get(document.body));
31631          
31632         this.progress = new Roo.bootstrap.Progress({
31633             cls : 'roo-document-manager-progress',
31634             active : true,
31635             striped : true
31636         });
31637         
31638         this.progress.render(this.progressDialog.getChildContainer());
31639         
31640         this.progressBar = new Roo.bootstrap.ProgressBar({
31641             cls : 'roo-document-manager-progress-bar',
31642             aria_valuenow : 0,
31643             aria_valuemin : 0,
31644             aria_valuemax : 12,
31645             panel : 'success'
31646         });
31647         
31648         this.progressBar.render(this.progress.getChildContainer());
31649     },
31650     
31651     onUploaderClick : function(e)
31652     {
31653         e.preventDefault();
31654      
31655         if(this.fireEvent('beforeselectfile', this) != false){
31656             this.selectorEl.dom.click();
31657         }
31658         
31659     },
31660     
31661     onFileSelected : function(e)
31662     {
31663         e.preventDefault();
31664         
31665         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31666             return;
31667         }
31668         
31669         Roo.each(this.selectorEl.dom.files, function(file){
31670             if(this.fireEvent('inspect', this, file) != false){
31671                 this.files.push(file);
31672             }
31673         }, this);
31674         
31675         this.queue();
31676         
31677     },
31678     
31679     queue : function()
31680     {
31681         this.selectorEl.dom.value = '';
31682         
31683         if(!this.files || !this.files.length){
31684             return;
31685         }
31686         
31687         if(this.boxes > 0 && this.files.length > this.boxes){
31688             this.files = this.files.slice(0, this.boxes);
31689         }
31690         
31691         this.uploader.show();
31692         
31693         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31694             this.uploader.hide();
31695         }
31696         
31697         var _this = this;
31698         
31699         var files = [];
31700         
31701         var docs = [];
31702         
31703         Roo.each(this.files, function(file){
31704             
31705             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31706                 var f = this.renderPreview(file);
31707                 files.push(f);
31708                 return;
31709             }
31710             
31711             if(file.type.indexOf('image') != -1){
31712                 this.delegates.push(
31713                     (function(){
31714                         _this.process(file);
31715                     }).createDelegate(this)
31716                 );
31717         
31718                 return;
31719             }
31720             
31721             docs.push(
31722                 (function(){
31723                     _this.process(file);
31724                 }).createDelegate(this)
31725             );
31726             
31727         }, this);
31728         
31729         this.files = files;
31730         
31731         this.delegates = this.delegates.concat(docs);
31732         
31733         if(!this.delegates.length){
31734             this.refresh();
31735             return;
31736         }
31737         
31738         this.progressBar.aria_valuemax = this.delegates.length;
31739         
31740         this.arrange();
31741         
31742         return;
31743     },
31744     
31745     arrange : function()
31746     {
31747         if(!this.delegates.length){
31748             this.progressDialog.hide();
31749             this.refresh();
31750             return;
31751         }
31752         
31753         var delegate = this.delegates.shift();
31754         
31755         this.progressDialog.show();
31756         
31757         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31758         
31759         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31760         
31761         delegate();
31762     },
31763     
31764     refresh : function()
31765     {
31766         this.uploader.show();
31767         
31768         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31769             this.uploader.hide();
31770         }
31771         
31772         Roo.isTouch ? this.closable(false) : this.closable(true);
31773         
31774         this.fireEvent('refresh', this);
31775     },
31776     
31777     onRemove : function(e, el, o)
31778     {
31779         e.preventDefault();
31780         
31781         this.fireEvent('remove', this, o);
31782         
31783     },
31784     
31785     remove : function(o)
31786     {
31787         var files = [];
31788         
31789         Roo.each(this.files, function(file){
31790             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31791                 files.push(file);
31792                 return;
31793             }
31794
31795             o.target.remove();
31796
31797         }, this);
31798         
31799         this.files = files;
31800         
31801         this.refresh();
31802     },
31803     
31804     clear : function()
31805     {
31806         Roo.each(this.files, function(file){
31807             if(!file.target){
31808                 return;
31809             }
31810             
31811             file.target.remove();
31812
31813         }, this);
31814         
31815         this.files = [];
31816         
31817         this.refresh();
31818     },
31819     
31820     onClick : function(e, el, o)
31821     {
31822         e.preventDefault();
31823         
31824         this.fireEvent('click', this, o);
31825         
31826     },
31827     
31828     closable : function(closable)
31829     {
31830         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31831             
31832             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31833             
31834             if(closable){
31835                 el.show();
31836                 return;
31837             }
31838             
31839             el.hide();
31840             
31841         }, this);
31842     },
31843     
31844     xhrOnLoad : function(xhr)
31845     {
31846         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31847             el.remove();
31848         }, this);
31849         
31850         if (xhr.readyState !== 4) {
31851             this.arrange();
31852             this.fireEvent('exception', this, xhr);
31853             return;
31854         }
31855
31856         var response = Roo.decode(xhr.responseText);
31857         
31858         if(!response.success){
31859             this.arrange();
31860             this.fireEvent('exception', this, xhr);
31861             return;
31862         }
31863         
31864         var file = this.renderPreview(response.data);
31865         
31866         this.files.push(file);
31867         
31868         this.arrange();
31869         
31870         this.fireEvent('afterupload', this, xhr);
31871         
31872     },
31873     
31874     xhrOnError : function(xhr)
31875     {
31876         Roo.log('xhr on error');
31877         
31878         var response = Roo.decode(xhr.responseText);
31879           
31880         Roo.log(response);
31881         
31882         this.arrange();
31883     },
31884     
31885     process : function(file)
31886     {
31887         if(this.fireEvent('process', this, file) !== false){
31888             if(this.editable && file.type.indexOf('image') != -1){
31889                 this.fireEvent('edit', this, file);
31890                 return;
31891             }
31892
31893             this.uploadStart(file, false);
31894
31895             return;
31896         }
31897         
31898     },
31899     
31900     uploadStart : function(file, crop)
31901     {
31902         this.xhr = new XMLHttpRequest();
31903         
31904         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31905             this.arrange();
31906             return;
31907         }
31908         
31909         file.xhr = this.xhr;
31910             
31911         this.managerEl.createChild({
31912             tag : 'div',
31913             cls : 'roo-document-manager-loading',
31914             cn : [
31915                 {
31916                     tag : 'div',
31917                     tooltip : file.name,
31918                     cls : 'roo-document-manager-thumb',
31919                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31920                 }
31921             ]
31922
31923         });
31924
31925         this.xhr.open(this.method, this.url, true);
31926         
31927         var headers = {
31928             "Accept": "application/json",
31929             "Cache-Control": "no-cache",
31930             "X-Requested-With": "XMLHttpRequest"
31931         };
31932         
31933         for (var headerName in headers) {
31934             var headerValue = headers[headerName];
31935             if (headerValue) {
31936                 this.xhr.setRequestHeader(headerName, headerValue);
31937             }
31938         }
31939         
31940         var _this = this;
31941         
31942         this.xhr.onload = function()
31943         {
31944             _this.xhrOnLoad(_this.xhr);
31945         }
31946         
31947         this.xhr.onerror = function()
31948         {
31949             _this.xhrOnError(_this.xhr);
31950         }
31951         
31952         var formData = new FormData();
31953
31954         formData.append('returnHTML', 'NO');
31955         
31956         if(crop){
31957             formData.append('crop', crop);
31958         }
31959         
31960         formData.append(this.paramName, file, file.name);
31961         
31962         var options = {
31963             file : file, 
31964             manually : false
31965         };
31966         
31967         if(this.fireEvent('prepare', this, formData, options) != false){
31968             
31969             if(options.manually){
31970                 return;
31971             }
31972             
31973             this.xhr.send(formData);
31974             return;
31975         };
31976         
31977         this.uploadCancel();
31978     },
31979     
31980     uploadCancel : function()
31981     {
31982         if (this.xhr) {
31983             this.xhr.abort();
31984         }
31985         
31986         this.delegates = [];
31987         
31988         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31989             el.remove();
31990         }, this);
31991         
31992         this.arrange();
31993     },
31994     
31995     renderPreview : function(file)
31996     {
31997         if(typeof(file.target) != 'undefined' && file.target){
31998             return file;
31999         }
32000         
32001         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32002         
32003         var previewEl = this.managerEl.createChild({
32004             tag : 'div',
32005             cls : 'roo-document-manager-preview',
32006             cn : [
32007                 {
32008                     tag : 'div',
32009                     tooltip : file[this.toolTipName],
32010                     cls : 'roo-document-manager-thumb',
32011                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32012                 },
32013                 {
32014                     tag : 'button',
32015                     cls : 'close',
32016                     html : '<i class="fa fa-times-circle"></i>'
32017                 }
32018             ]
32019         });
32020
32021         var close = previewEl.select('button.close', true).first();
32022
32023         close.on('click', this.onRemove, this, file);
32024
32025         file.target = previewEl;
32026
32027         var image = previewEl.select('img', true).first();
32028         
32029         var _this = this;
32030         
32031         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32032         
32033         image.on('click', this.onClick, this, file);
32034         
32035         this.fireEvent('previewrendered', this, file);
32036         
32037         return file;
32038         
32039     },
32040     
32041     onPreviewLoad : function(file, image)
32042     {
32043         if(typeof(file.target) == 'undefined' || !file.target){
32044             return;
32045         }
32046         
32047         var width = image.dom.naturalWidth || image.dom.width;
32048         var height = image.dom.naturalHeight || image.dom.height;
32049         
32050         if(!this.previewResize) {
32051             return;
32052         }
32053         
32054         if(width > height){
32055             file.target.addClass('wide');
32056             return;
32057         }
32058         
32059         file.target.addClass('tall');
32060         return;
32061         
32062     },
32063     
32064     uploadFromSource : function(file, crop)
32065     {
32066         this.xhr = new XMLHttpRequest();
32067         
32068         this.managerEl.createChild({
32069             tag : 'div',
32070             cls : 'roo-document-manager-loading',
32071             cn : [
32072                 {
32073                     tag : 'div',
32074                     tooltip : file.name,
32075                     cls : 'roo-document-manager-thumb',
32076                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32077                 }
32078             ]
32079
32080         });
32081
32082         this.xhr.open(this.method, this.url, true);
32083         
32084         var headers = {
32085             "Accept": "application/json",
32086             "Cache-Control": "no-cache",
32087             "X-Requested-With": "XMLHttpRequest"
32088         };
32089         
32090         for (var headerName in headers) {
32091             var headerValue = headers[headerName];
32092             if (headerValue) {
32093                 this.xhr.setRequestHeader(headerName, headerValue);
32094             }
32095         }
32096         
32097         var _this = this;
32098         
32099         this.xhr.onload = function()
32100         {
32101             _this.xhrOnLoad(_this.xhr);
32102         }
32103         
32104         this.xhr.onerror = function()
32105         {
32106             _this.xhrOnError(_this.xhr);
32107         }
32108         
32109         var formData = new FormData();
32110
32111         formData.append('returnHTML', 'NO');
32112         
32113         formData.append('crop', crop);
32114         
32115         if(typeof(file.filename) != 'undefined'){
32116             formData.append('filename', file.filename);
32117         }
32118         
32119         if(typeof(file.mimetype) != 'undefined'){
32120             formData.append('mimetype', file.mimetype);
32121         }
32122         
32123         Roo.log(formData);
32124         
32125         if(this.fireEvent('prepare', this, formData) != false){
32126             this.xhr.send(formData);
32127         };
32128     }
32129 });
32130
32131 /*
32132 * Licence: LGPL
32133 */
32134
32135 /**
32136  * @class Roo.bootstrap.DocumentViewer
32137  * @extends Roo.bootstrap.Component
32138  * Bootstrap DocumentViewer class
32139  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32140  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32141  * 
32142  * @constructor
32143  * Create a new DocumentViewer
32144  * @param {Object} config The config object
32145  */
32146
32147 Roo.bootstrap.DocumentViewer = function(config){
32148     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32149     
32150     this.addEvents({
32151         /**
32152          * @event initial
32153          * Fire after initEvent
32154          * @param {Roo.bootstrap.DocumentViewer} this
32155          */
32156         "initial" : true,
32157         /**
32158          * @event click
32159          * Fire after click
32160          * @param {Roo.bootstrap.DocumentViewer} this
32161          */
32162         "click" : true,
32163         /**
32164          * @event download
32165          * Fire after download button
32166          * @param {Roo.bootstrap.DocumentViewer} this
32167          */
32168         "download" : true,
32169         /**
32170          * @event trash
32171          * Fire after trash button
32172          * @param {Roo.bootstrap.DocumentViewer} this
32173          */
32174         "trash" : true
32175         
32176     });
32177 };
32178
32179 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32180     
32181     showDownload : true,
32182     
32183     showTrash : true,
32184     
32185     getAutoCreate : function()
32186     {
32187         var cfg = {
32188             tag : 'div',
32189             cls : 'roo-document-viewer',
32190             cn : [
32191                 {
32192                     tag : 'div',
32193                     cls : 'roo-document-viewer-body',
32194                     cn : [
32195                         {
32196                             tag : 'div',
32197                             cls : 'roo-document-viewer-thumb',
32198                             cn : [
32199                                 {
32200                                     tag : 'img',
32201                                     cls : 'roo-document-viewer-image'
32202                                 }
32203                             ]
32204                         }
32205                     ]
32206                 },
32207                 {
32208                     tag : 'div',
32209                     cls : 'roo-document-viewer-footer',
32210                     cn : {
32211                         tag : 'div',
32212                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32213                         cn : [
32214                             {
32215                                 tag : 'div',
32216                                 cls : 'btn-group roo-document-viewer-download',
32217                                 cn : [
32218                                     {
32219                                         tag : 'button',
32220                                         cls : 'btn btn-default',
32221                                         html : '<i class="fa fa-download"></i>'
32222                                     }
32223                                 ]
32224                             },
32225                             {
32226                                 tag : 'div',
32227                                 cls : 'btn-group roo-document-viewer-trash',
32228                                 cn : [
32229                                     {
32230                                         tag : 'button',
32231                                         cls : 'btn btn-default',
32232                                         html : '<i class="fa fa-trash"></i>'
32233                                     }
32234                                 ]
32235                             }
32236                         ]
32237                     }
32238                 }
32239             ]
32240         };
32241         
32242         return cfg;
32243     },
32244     
32245     initEvents : function()
32246     {
32247         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32248         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32249         
32250         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32251         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32252         
32253         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32254         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32255         
32256         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32257         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32258         
32259         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32260         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32261         
32262         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32263         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32264         
32265         this.bodyEl.on('click', this.onClick, this);
32266         this.downloadBtn.on('click', this.onDownload, this);
32267         this.trashBtn.on('click', this.onTrash, this);
32268         
32269         this.downloadBtn.hide();
32270         this.trashBtn.hide();
32271         
32272         if(this.showDownload){
32273             this.downloadBtn.show();
32274         }
32275         
32276         if(this.showTrash){
32277             this.trashBtn.show();
32278         }
32279         
32280         if(!this.showDownload && !this.showTrash) {
32281             this.footerEl.hide();
32282         }
32283         
32284     },
32285     
32286     initial : function()
32287     {
32288         this.fireEvent('initial', this);
32289         
32290     },
32291     
32292     onClick : function(e)
32293     {
32294         e.preventDefault();
32295         
32296         this.fireEvent('click', this);
32297     },
32298     
32299     onDownload : function(e)
32300     {
32301         e.preventDefault();
32302         
32303         this.fireEvent('download', this);
32304     },
32305     
32306     onTrash : function(e)
32307     {
32308         e.preventDefault();
32309         
32310         this.fireEvent('trash', this);
32311     }
32312     
32313 });
32314 /*
32315  * - LGPL
32316  *
32317  * nav progress bar
32318  * 
32319  */
32320
32321 /**
32322  * @class Roo.bootstrap.NavProgressBar
32323  * @extends Roo.bootstrap.Component
32324  * Bootstrap NavProgressBar class
32325  * 
32326  * @constructor
32327  * Create a new nav progress bar
32328  * @param {Object} config The config object
32329  */
32330
32331 Roo.bootstrap.NavProgressBar = function(config){
32332     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32333
32334     this.bullets = this.bullets || [];
32335    
32336 //    Roo.bootstrap.NavProgressBar.register(this);
32337      this.addEvents({
32338         /**
32339              * @event changed
32340              * Fires when the active item changes
32341              * @param {Roo.bootstrap.NavProgressBar} this
32342              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32343              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32344          */
32345         'changed': true
32346      });
32347     
32348 };
32349
32350 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32351     
32352     bullets : [],
32353     barItems : [],
32354     
32355     getAutoCreate : function()
32356     {
32357         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32358         
32359         cfg = {
32360             tag : 'div',
32361             cls : 'roo-navigation-bar-group',
32362             cn : [
32363                 {
32364                     tag : 'div',
32365                     cls : 'roo-navigation-top-bar'
32366                 },
32367                 {
32368                     tag : 'div',
32369                     cls : 'roo-navigation-bullets-bar',
32370                     cn : [
32371                         {
32372                             tag : 'ul',
32373                             cls : 'roo-navigation-bar'
32374                         }
32375                     ]
32376                 },
32377                 
32378                 {
32379                     tag : 'div',
32380                     cls : 'roo-navigation-bottom-bar'
32381                 }
32382             ]
32383             
32384         };
32385         
32386         return cfg;
32387         
32388     },
32389     
32390     initEvents: function() 
32391     {
32392         
32393     },
32394     
32395     onRender : function(ct, position) 
32396     {
32397         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32398         
32399         if(this.bullets.length){
32400             Roo.each(this.bullets, function(b){
32401                this.addItem(b);
32402             }, this);
32403         }
32404         
32405         this.format();
32406         
32407     },
32408     
32409     addItem : function(cfg)
32410     {
32411         var item = new Roo.bootstrap.NavProgressItem(cfg);
32412         
32413         item.parentId = this.id;
32414         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32415         
32416         if(cfg.html){
32417             var top = new Roo.bootstrap.Element({
32418                 tag : 'div',
32419                 cls : 'roo-navigation-bar-text'
32420             });
32421             
32422             var bottom = new Roo.bootstrap.Element({
32423                 tag : 'div',
32424                 cls : 'roo-navigation-bar-text'
32425             });
32426             
32427             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32428             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32429             
32430             var topText = new Roo.bootstrap.Element({
32431                 tag : 'span',
32432                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32433             });
32434             
32435             var bottomText = new Roo.bootstrap.Element({
32436                 tag : 'span',
32437                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32438             });
32439             
32440             topText.onRender(top.el, null);
32441             bottomText.onRender(bottom.el, null);
32442             
32443             item.topEl = top;
32444             item.bottomEl = bottom;
32445         }
32446         
32447         this.barItems.push(item);
32448         
32449         return item;
32450     },
32451     
32452     getActive : function()
32453     {
32454         var active = false;
32455         
32456         Roo.each(this.barItems, function(v){
32457             
32458             if (!v.isActive()) {
32459                 return;
32460             }
32461             
32462             active = v;
32463             return false;
32464             
32465         });
32466         
32467         return active;
32468     },
32469     
32470     setActiveItem : function(item)
32471     {
32472         var prev = false;
32473         
32474         Roo.each(this.barItems, function(v){
32475             if (v.rid == item.rid) {
32476                 return ;
32477             }
32478             
32479             if (v.isActive()) {
32480                 v.setActive(false);
32481                 prev = v;
32482             }
32483         });
32484
32485         item.setActive(true);
32486         
32487         this.fireEvent('changed', this, item, prev);
32488     },
32489     
32490     getBarItem: function(rid)
32491     {
32492         var ret = false;
32493         
32494         Roo.each(this.barItems, function(e) {
32495             if (e.rid != rid) {
32496                 return;
32497             }
32498             
32499             ret =  e;
32500             return false;
32501         });
32502         
32503         return ret;
32504     },
32505     
32506     indexOfItem : function(item)
32507     {
32508         var index = false;
32509         
32510         Roo.each(this.barItems, function(v, i){
32511             
32512             if (v.rid != item.rid) {
32513                 return;
32514             }
32515             
32516             index = i;
32517             return false
32518         });
32519         
32520         return index;
32521     },
32522     
32523     setActiveNext : function()
32524     {
32525         var i = this.indexOfItem(this.getActive());
32526         
32527         if (i > this.barItems.length) {
32528             return;
32529         }
32530         
32531         this.setActiveItem(this.barItems[i+1]);
32532     },
32533     
32534     setActivePrev : function()
32535     {
32536         var i = this.indexOfItem(this.getActive());
32537         
32538         if (i  < 1) {
32539             return;
32540         }
32541         
32542         this.setActiveItem(this.barItems[i-1]);
32543     },
32544     
32545     format : function()
32546     {
32547         if(!this.barItems.length){
32548             return;
32549         }
32550      
32551         var width = 100 / this.barItems.length;
32552         
32553         Roo.each(this.barItems, function(i){
32554             i.el.setStyle('width', width + '%');
32555             i.topEl.el.setStyle('width', width + '%');
32556             i.bottomEl.el.setStyle('width', width + '%');
32557         }, this);
32558         
32559     }
32560     
32561 });
32562 /*
32563  * - LGPL
32564  *
32565  * Nav Progress Item
32566  * 
32567  */
32568
32569 /**
32570  * @class Roo.bootstrap.NavProgressItem
32571  * @extends Roo.bootstrap.Component
32572  * Bootstrap NavProgressItem class
32573  * @cfg {String} rid the reference id
32574  * @cfg {Boolean} active (true|false) Is item active default false
32575  * @cfg {Boolean} disabled (true|false) Is item active default false
32576  * @cfg {String} html
32577  * @cfg {String} position (top|bottom) text position default bottom
32578  * @cfg {String} icon show icon instead of number
32579  * 
32580  * @constructor
32581  * Create a new NavProgressItem
32582  * @param {Object} config The config object
32583  */
32584 Roo.bootstrap.NavProgressItem = function(config){
32585     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32586     this.addEvents({
32587         // raw events
32588         /**
32589          * @event click
32590          * The raw click event for the entire grid.
32591          * @param {Roo.bootstrap.NavProgressItem} this
32592          * @param {Roo.EventObject} e
32593          */
32594         "click" : true
32595     });
32596    
32597 };
32598
32599 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32600     
32601     rid : '',
32602     active : false,
32603     disabled : false,
32604     html : '',
32605     position : 'bottom',
32606     icon : false,
32607     
32608     getAutoCreate : function()
32609     {
32610         var iconCls = 'roo-navigation-bar-item-icon';
32611         
32612         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32613         
32614         var cfg = {
32615             tag: 'li',
32616             cls: 'roo-navigation-bar-item',
32617             cn : [
32618                 {
32619                     tag : 'i',
32620                     cls : iconCls
32621                 }
32622             ]
32623         };
32624         
32625         if(this.active){
32626             cfg.cls += ' active';
32627         }
32628         if(this.disabled){
32629             cfg.cls += ' disabled';
32630         }
32631         
32632         return cfg;
32633     },
32634     
32635     disable : function()
32636     {
32637         this.setDisabled(true);
32638     },
32639     
32640     enable : function()
32641     {
32642         this.setDisabled(false);
32643     },
32644     
32645     initEvents: function() 
32646     {
32647         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32648         
32649         this.iconEl.on('click', this.onClick, this);
32650     },
32651     
32652     onClick : function(e)
32653     {
32654         e.preventDefault();
32655         
32656         if(this.disabled){
32657             return;
32658         }
32659         
32660         if(this.fireEvent('click', this, e) === false){
32661             return;
32662         };
32663         
32664         this.parent().setActiveItem(this);
32665     },
32666     
32667     isActive: function () 
32668     {
32669         return this.active;
32670     },
32671     
32672     setActive : function(state)
32673     {
32674         if(this.active == state){
32675             return;
32676         }
32677         
32678         this.active = state;
32679         
32680         if (state) {
32681             this.el.addClass('active');
32682             return;
32683         }
32684         
32685         this.el.removeClass('active');
32686         
32687         return;
32688     },
32689     
32690     setDisabled : function(state)
32691     {
32692         if(this.disabled == state){
32693             return;
32694         }
32695         
32696         this.disabled = state;
32697         
32698         if (state) {
32699             this.el.addClass('disabled');
32700             return;
32701         }
32702         
32703         this.el.removeClass('disabled');
32704     },
32705     
32706     tooltipEl : function()
32707     {
32708         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32709     }
32710 });
32711  
32712
32713  /*
32714  * - LGPL
32715  *
32716  * FieldLabel
32717  * 
32718  */
32719
32720 /**
32721  * @class Roo.bootstrap.FieldLabel
32722  * @extends Roo.bootstrap.Component
32723  * Bootstrap FieldLabel class
32724  * @cfg {String} html contents of the element
32725  * @cfg {String} tag tag of the element default label
32726  * @cfg {String} cls class of the element
32727  * @cfg {String} target label target 
32728  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32729  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32730  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32731  * @cfg {String} iconTooltip default "This field is required"
32732  * @cfg {String} indicatorpos (left|right) default left
32733  * 
32734  * @constructor
32735  * Create a new FieldLabel
32736  * @param {Object} config The config object
32737  */
32738
32739 Roo.bootstrap.FieldLabel = function(config){
32740     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32741     
32742     this.addEvents({
32743             /**
32744              * @event invalid
32745              * Fires after the field has been marked as invalid.
32746              * @param {Roo.form.FieldLabel} this
32747              * @param {String} msg The validation message
32748              */
32749             invalid : true,
32750             /**
32751              * @event valid
32752              * Fires after the field has been validated with no errors.
32753              * @param {Roo.form.FieldLabel} this
32754              */
32755             valid : true
32756         });
32757 };
32758
32759 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32760     
32761     tag: 'label',
32762     cls: '',
32763     html: '',
32764     target: '',
32765     allowBlank : true,
32766     invalidClass : 'has-warning',
32767     validClass : 'has-success',
32768     iconTooltip : 'This field is required',
32769     indicatorpos : 'left',
32770     
32771     getAutoCreate : function(){
32772         
32773         var cls = "";
32774         if (!this.allowBlank) {
32775             cls  = "visible";
32776         }
32777         
32778         var cfg = {
32779             tag : this.tag,
32780             cls : 'roo-bootstrap-field-label ' + this.cls,
32781             for : this.target,
32782             cn : [
32783                 {
32784                     tag : 'i',
32785                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32786                     tooltip : this.iconTooltip
32787                 },
32788                 {
32789                     tag : 'span',
32790                     html : this.html
32791                 }
32792             ] 
32793         };
32794         
32795         if(this.indicatorpos == 'right'){
32796             var cfg = {
32797                 tag : this.tag,
32798                 cls : 'roo-bootstrap-field-label ' + this.cls,
32799                 for : this.target,
32800                 cn : [
32801                     {
32802                         tag : 'span',
32803                         html : this.html
32804                     },
32805                     {
32806                         tag : 'i',
32807                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32808                         tooltip : this.iconTooltip
32809                     }
32810                 ] 
32811             };
32812         }
32813         
32814         return cfg;
32815     },
32816     
32817     initEvents: function() 
32818     {
32819         Roo.bootstrap.Element.superclass.initEvents.call(this);
32820         
32821         this.indicator = this.indicatorEl();
32822         
32823         if(this.indicator){
32824             this.indicator.removeClass('visible');
32825             this.indicator.addClass('invisible');
32826         }
32827         
32828         Roo.bootstrap.FieldLabel.register(this);
32829     },
32830     
32831     indicatorEl : function()
32832     {
32833         var indicator = this.el.select('i.roo-required-indicator',true).first();
32834         
32835         if(!indicator){
32836             return false;
32837         }
32838         
32839         return indicator;
32840         
32841     },
32842     
32843     /**
32844      * Mark this field as valid
32845      */
32846     markValid : function()
32847     {
32848         if(this.indicator){
32849             this.indicator.removeClass('visible');
32850             this.indicator.addClass('invisible');
32851         }
32852         if (Roo.bootstrap.version == 3) {
32853             this.el.removeClass(this.invalidClass);
32854             this.el.addClass(this.validClass);
32855         } else {
32856             this.el.removeClass('is-invalid');
32857             this.el.addClass('is-valid');
32858         }
32859         
32860         
32861         this.fireEvent('valid', this);
32862     },
32863     
32864     /**
32865      * Mark this field as invalid
32866      * @param {String} msg The validation message
32867      */
32868     markInvalid : function(msg)
32869     {
32870         if(this.indicator){
32871             this.indicator.removeClass('invisible');
32872             this.indicator.addClass('visible');
32873         }
32874           if (Roo.bootstrap.version == 3) {
32875             this.el.removeClass(this.validClass);
32876             this.el.addClass(this.invalidClass);
32877         } else {
32878             this.el.removeClass('is-valid');
32879             this.el.addClass('is-invalid');
32880         }
32881         
32882         
32883         this.fireEvent('invalid', this, msg);
32884     }
32885     
32886    
32887 });
32888
32889 Roo.apply(Roo.bootstrap.FieldLabel, {
32890     
32891     groups: {},
32892     
32893      /**
32894     * register a FieldLabel Group
32895     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32896     */
32897     register : function(label)
32898     {
32899         if(this.groups.hasOwnProperty(label.target)){
32900             return;
32901         }
32902      
32903         this.groups[label.target] = label;
32904         
32905     },
32906     /**
32907     * fetch a FieldLabel Group based on the target
32908     * @param {string} target
32909     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32910     */
32911     get: function(target) {
32912         if (typeof(this.groups[target]) == 'undefined') {
32913             return false;
32914         }
32915         
32916         return this.groups[target] ;
32917     }
32918 });
32919
32920  
32921
32922  /*
32923  * - LGPL
32924  *
32925  * page DateSplitField.
32926  * 
32927  */
32928
32929
32930 /**
32931  * @class Roo.bootstrap.DateSplitField
32932  * @extends Roo.bootstrap.Component
32933  * Bootstrap DateSplitField class
32934  * @cfg {string} fieldLabel - the label associated
32935  * @cfg {Number} labelWidth set the width of label (0-12)
32936  * @cfg {String} labelAlign (top|left)
32937  * @cfg {Boolean} dayAllowBlank (true|false) default false
32938  * @cfg {Boolean} monthAllowBlank (true|false) default false
32939  * @cfg {Boolean} yearAllowBlank (true|false) default false
32940  * @cfg {string} dayPlaceholder 
32941  * @cfg {string} monthPlaceholder
32942  * @cfg {string} yearPlaceholder
32943  * @cfg {string} dayFormat default 'd'
32944  * @cfg {string} monthFormat default 'm'
32945  * @cfg {string} yearFormat default 'Y'
32946  * @cfg {Number} labellg set the width of label (1-12)
32947  * @cfg {Number} labelmd set the width of label (1-12)
32948  * @cfg {Number} labelsm set the width of label (1-12)
32949  * @cfg {Number} labelxs set the width of label (1-12)
32950
32951  *     
32952  * @constructor
32953  * Create a new DateSplitField
32954  * @param {Object} config The config object
32955  */
32956
32957 Roo.bootstrap.DateSplitField = function(config){
32958     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32959     
32960     this.addEvents({
32961         // raw events
32962          /**
32963          * @event years
32964          * getting the data of years
32965          * @param {Roo.bootstrap.DateSplitField} this
32966          * @param {Object} years
32967          */
32968         "years" : true,
32969         /**
32970          * @event days
32971          * getting the data of days
32972          * @param {Roo.bootstrap.DateSplitField} this
32973          * @param {Object} days
32974          */
32975         "days" : true,
32976         /**
32977          * @event invalid
32978          * Fires after the field has been marked as invalid.
32979          * @param {Roo.form.Field} this
32980          * @param {String} msg The validation message
32981          */
32982         invalid : true,
32983        /**
32984          * @event valid
32985          * Fires after the field has been validated with no errors.
32986          * @param {Roo.form.Field} this
32987          */
32988         valid : true
32989     });
32990 };
32991
32992 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32993     
32994     fieldLabel : '',
32995     labelAlign : 'top',
32996     labelWidth : 3,
32997     dayAllowBlank : false,
32998     monthAllowBlank : false,
32999     yearAllowBlank : false,
33000     dayPlaceholder : '',
33001     monthPlaceholder : '',
33002     yearPlaceholder : '',
33003     dayFormat : 'd',
33004     monthFormat : 'm',
33005     yearFormat : 'Y',
33006     isFormField : true,
33007     labellg : 0,
33008     labelmd : 0,
33009     labelsm : 0,
33010     labelxs : 0,
33011     
33012     getAutoCreate : function()
33013     {
33014         var cfg = {
33015             tag : 'div',
33016             cls : 'row roo-date-split-field-group',
33017             cn : [
33018                 {
33019                     tag : 'input',
33020                     type : 'hidden',
33021                     cls : 'form-hidden-field roo-date-split-field-group-value',
33022                     name : this.name
33023                 }
33024             ]
33025         };
33026         
33027         var labelCls = 'col-md-12';
33028         var contentCls = 'col-md-4';
33029         
33030         if(this.fieldLabel){
33031             
33032             var label = {
33033                 tag : 'div',
33034                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33035                 cn : [
33036                     {
33037                         tag : 'label',
33038                         html : this.fieldLabel
33039                     }
33040                 ]
33041             };
33042             
33043             if(this.labelAlign == 'left'){
33044             
33045                 if(this.labelWidth > 12){
33046                     label.style = "width: " + this.labelWidth + 'px';
33047                 }
33048
33049                 if(this.labelWidth < 13 && this.labelmd == 0){
33050                     this.labelmd = this.labelWidth;
33051                 }
33052
33053                 if(this.labellg > 0){
33054                     labelCls = ' col-lg-' + this.labellg;
33055                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33056                 }
33057
33058                 if(this.labelmd > 0){
33059                     labelCls = ' col-md-' + this.labelmd;
33060                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33061                 }
33062
33063                 if(this.labelsm > 0){
33064                     labelCls = ' col-sm-' + this.labelsm;
33065                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33066                 }
33067
33068                 if(this.labelxs > 0){
33069                     labelCls = ' col-xs-' + this.labelxs;
33070                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33071                 }
33072             }
33073             
33074             label.cls += ' ' + labelCls;
33075             
33076             cfg.cn.push(label);
33077         }
33078         
33079         Roo.each(['day', 'month', 'year'], function(t){
33080             cfg.cn.push({
33081                 tag : 'div',
33082                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33083             });
33084         }, this);
33085         
33086         return cfg;
33087     },
33088     
33089     inputEl: function ()
33090     {
33091         return this.el.select('.roo-date-split-field-group-value', true).first();
33092     },
33093     
33094     onRender : function(ct, position) 
33095     {
33096         var _this = this;
33097         
33098         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33099         
33100         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33101         
33102         this.dayField = new Roo.bootstrap.ComboBox({
33103             allowBlank : this.dayAllowBlank,
33104             alwaysQuery : true,
33105             displayField : 'value',
33106             editable : false,
33107             fieldLabel : '',
33108             forceSelection : true,
33109             mode : 'local',
33110             placeholder : this.dayPlaceholder,
33111             selectOnFocus : true,
33112             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33113             triggerAction : 'all',
33114             typeAhead : true,
33115             valueField : 'value',
33116             store : new Roo.data.SimpleStore({
33117                 data : (function() {    
33118                     var days = [];
33119                     _this.fireEvent('days', _this, days);
33120                     return days;
33121                 })(),
33122                 fields : [ 'value' ]
33123             }),
33124             listeners : {
33125                 select : function (_self, record, index)
33126                 {
33127                     _this.setValue(_this.getValue());
33128                 }
33129             }
33130         });
33131
33132         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33133         
33134         this.monthField = new Roo.bootstrap.MonthField({
33135             after : '<i class=\"fa fa-calendar\"></i>',
33136             allowBlank : this.monthAllowBlank,
33137             placeholder : this.monthPlaceholder,
33138             readOnly : true,
33139             listeners : {
33140                 render : function (_self)
33141                 {
33142                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33143                         e.preventDefault();
33144                         _self.focus();
33145                     });
33146                 },
33147                 select : function (_self, oldvalue, newvalue)
33148                 {
33149                     _this.setValue(_this.getValue());
33150                 }
33151             }
33152         });
33153         
33154         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33155         
33156         this.yearField = new Roo.bootstrap.ComboBox({
33157             allowBlank : this.yearAllowBlank,
33158             alwaysQuery : true,
33159             displayField : 'value',
33160             editable : false,
33161             fieldLabel : '',
33162             forceSelection : true,
33163             mode : 'local',
33164             placeholder : this.yearPlaceholder,
33165             selectOnFocus : true,
33166             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33167             triggerAction : 'all',
33168             typeAhead : true,
33169             valueField : 'value',
33170             store : new Roo.data.SimpleStore({
33171                 data : (function() {
33172                     var years = [];
33173                     _this.fireEvent('years', _this, years);
33174                     return years;
33175                 })(),
33176                 fields : [ 'value' ]
33177             }),
33178             listeners : {
33179                 select : function (_self, record, index)
33180                 {
33181                     _this.setValue(_this.getValue());
33182                 }
33183             }
33184         });
33185
33186         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33187     },
33188     
33189     setValue : function(v, format)
33190     {
33191         this.inputEl.dom.value = v;
33192         
33193         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33194         
33195         var d = Date.parseDate(v, f);
33196         
33197         if(!d){
33198             this.validate();
33199             return;
33200         }
33201         
33202         this.setDay(d.format(this.dayFormat));
33203         this.setMonth(d.format(this.monthFormat));
33204         this.setYear(d.format(this.yearFormat));
33205         
33206         this.validate();
33207         
33208         return;
33209     },
33210     
33211     setDay : function(v)
33212     {
33213         this.dayField.setValue(v);
33214         this.inputEl.dom.value = this.getValue();
33215         this.validate();
33216         return;
33217     },
33218     
33219     setMonth : function(v)
33220     {
33221         this.monthField.setValue(v, true);
33222         this.inputEl.dom.value = this.getValue();
33223         this.validate();
33224         return;
33225     },
33226     
33227     setYear : function(v)
33228     {
33229         this.yearField.setValue(v);
33230         this.inputEl.dom.value = this.getValue();
33231         this.validate();
33232         return;
33233     },
33234     
33235     getDay : function()
33236     {
33237         return this.dayField.getValue();
33238     },
33239     
33240     getMonth : function()
33241     {
33242         return this.monthField.getValue();
33243     },
33244     
33245     getYear : function()
33246     {
33247         return this.yearField.getValue();
33248     },
33249     
33250     getValue : function()
33251     {
33252         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33253         
33254         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33255         
33256         return date;
33257     },
33258     
33259     reset : function()
33260     {
33261         this.setDay('');
33262         this.setMonth('');
33263         this.setYear('');
33264         this.inputEl.dom.value = '';
33265         this.validate();
33266         return;
33267     },
33268     
33269     validate : function()
33270     {
33271         var d = this.dayField.validate();
33272         var m = this.monthField.validate();
33273         var y = this.yearField.validate();
33274         
33275         var valid = true;
33276         
33277         if(
33278                 (!this.dayAllowBlank && !d) ||
33279                 (!this.monthAllowBlank && !m) ||
33280                 (!this.yearAllowBlank && !y)
33281         ){
33282             valid = false;
33283         }
33284         
33285         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33286             return valid;
33287         }
33288         
33289         if(valid){
33290             this.markValid();
33291             return valid;
33292         }
33293         
33294         this.markInvalid();
33295         
33296         return valid;
33297     },
33298     
33299     markValid : function()
33300     {
33301         
33302         var label = this.el.select('label', true).first();
33303         var icon = this.el.select('i.fa-star', true).first();
33304
33305         if(label && icon){
33306             icon.remove();
33307         }
33308         
33309         this.fireEvent('valid', this);
33310     },
33311     
33312      /**
33313      * Mark this field as invalid
33314      * @param {String} msg The validation message
33315      */
33316     markInvalid : function(msg)
33317     {
33318         
33319         var label = this.el.select('label', true).first();
33320         var icon = this.el.select('i.fa-star', true).first();
33321
33322         if(label && !icon){
33323             this.el.select('.roo-date-split-field-label', true).createChild({
33324                 tag : 'i',
33325                 cls : 'text-danger fa fa-lg fa-star',
33326                 tooltip : 'This field is required',
33327                 style : 'margin-right:5px;'
33328             }, label, true);
33329         }
33330         
33331         this.fireEvent('invalid', this, msg);
33332     },
33333     
33334     clearInvalid : function()
33335     {
33336         var label = this.el.select('label', true).first();
33337         var icon = this.el.select('i.fa-star', true).first();
33338
33339         if(label && icon){
33340             icon.remove();
33341         }
33342         
33343         this.fireEvent('valid', this);
33344     },
33345     
33346     getName: function()
33347     {
33348         return this.name;
33349     }
33350     
33351 });
33352
33353  /**
33354  *
33355  * This is based on 
33356  * http://masonry.desandro.com
33357  *
33358  * The idea is to render all the bricks based on vertical width...
33359  *
33360  * The original code extends 'outlayer' - we might need to use that....
33361  * 
33362  */
33363
33364
33365 /**
33366  * @class Roo.bootstrap.LayoutMasonry
33367  * @extends Roo.bootstrap.Component
33368  * Bootstrap Layout Masonry class
33369  * 
33370  * @constructor
33371  * Create a new Element
33372  * @param {Object} config The config object
33373  */
33374
33375 Roo.bootstrap.LayoutMasonry = function(config){
33376     
33377     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33378     
33379     this.bricks = [];
33380     
33381     Roo.bootstrap.LayoutMasonry.register(this);
33382     
33383     this.addEvents({
33384         // raw events
33385         /**
33386          * @event layout
33387          * Fire after layout the items
33388          * @param {Roo.bootstrap.LayoutMasonry} this
33389          * @param {Roo.EventObject} e
33390          */
33391         "layout" : true
33392     });
33393     
33394 };
33395
33396 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33397     
33398     /**
33399      * @cfg {Boolean} isLayoutInstant = no animation?
33400      */   
33401     isLayoutInstant : false, // needed?
33402    
33403     /**
33404      * @cfg {Number} boxWidth  width of the columns
33405      */   
33406     boxWidth : 450,
33407     
33408       /**
33409      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33410      */   
33411     boxHeight : 0,
33412     
33413     /**
33414      * @cfg {Number} padWidth padding below box..
33415      */   
33416     padWidth : 10, 
33417     
33418     /**
33419      * @cfg {Number} gutter gutter width..
33420      */   
33421     gutter : 10,
33422     
33423      /**
33424      * @cfg {Number} maxCols maximum number of columns
33425      */   
33426     
33427     maxCols: 0,
33428     
33429     /**
33430      * @cfg {Boolean} isAutoInitial defalut true
33431      */   
33432     isAutoInitial : true, 
33433     
33434     containerWidth: 0,
33435     
33436     /**
33437      * @cfg {Boolean} isHorizontal defalut false
33438      */   
33439     isHorizontal : false, 
33440
33441     currentSize : null,
33442     
33443     tag: 'div',
33444     
33445     cls: '',
33446     
33447     bricks: null, //CompositeElement
33448     
33449     cols : 1,
33450     
33451     _isLayoutInited : false,
33452     
33453 //    isAlternative : false, // only use for vertical layout...
33454     
33455     /**
33456      * @cfg {Number} alternativePadWidth padding below box..
33457      */   
33458     alternativePadWidth : 50,
33459     
33460     selectedBrick : [],
33461     
33462     getAutoCreate : function(){
33463         
33464         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33465         
33466         var cfg = {
33467             tag: this.tag,
33468             cls: 'blog-masonary-wrapper ' + this.cls,
33469             cn : {
33470                 cls : 'mas-boxes masonary'
33471             }
33472         };
33473         
33474         return cfg;
33475     },
33476     
33477     getChildContainer: function( )
33478     {
33479         if (this.boxesEl) {
33480             return this.boxesEl;
33481         }
33482         
33483         this.boxesEl = this.el.select('.mas-boxes').first();
33484         
33485         return this.boxesEl;
33486     },
33487     
33488     
33489     initEvents : function()
33490     {
33491         var _this = this;
33492         
33493         if(this.isAutoInitial){
33494             Roo.log('hook children rendered');
33495             this.on('childrenrendered', function() {
33496                 Roo.log('children rendered');
33497                 _this.initial();
33498             } ,this);
33499         }
33500     },
33501     
33502     initial : function()
33503     {
33504         this.selectedBrick = [];
33505         
33506         this.currentSize = this.el.getBox(true);
33507         
33508         Roo.EventManager.onWindowResize(this.resize, this); 
33509
33510         if(!this.isAutoInitial){
33511             this.layout();
33512             return;
33513         }
33514         
33515         this.layout();
33516         
33517         return;
33518         //this.layout.defer(500,this);
33519         
33520     },
33521     
33522     resize : function()
33523     {
33524         var cs = this.el.getBox(true);
33525         
33526         if (
33527                 this.currentSize.width == cs.width && 
33528                 this.currentSize.x == cs.x && 
33529                 this.currentSize.height == cs.height && 
33530                 this.currentSize.y == cs.y 
33531         ) {
33532             Roo.log("no change in with or X or Y");
33533             return;
33534         }
33535         
33536         this.currentSize = cs;
33537         
33538         this.layout();
33539         
33540     },
33541     
33542     layout : function()
33543     {   
33544         this._resetLayout();
33545         
33546         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33547         
33548         this.layoutItems( isInstant );
33549       
33550         this._isLayoutInited = true;
33551         
33552         this.fireEvent('layout', this);
33553         
33554     },
33555     
33556     _resetLayout : function()
33557     {
33558         if(this.isHorizontal){
33559             this.horizontalMeasureColumns();
33560             return;
33561         }
33562         
33563         this.verticalMeasureColumns();
33564         
33565     },
33566     
33567     verticalMeasureColumns : function()
33568     {
33569         this.getContainerWidth();
33570         
33571 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33572 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33573 //            return;
33574 //        }
33575         
33576         var boxWidth = this.boxWidth + this.padWidth;
33577         
33578         if(this.containerWidth < this.boxWidth){
33579             boxWidth = this.containerWidth
33580         }
33581         
33582         var containerWidth = this.containerWidth;
33583         
33584         var cols = Math.floor(containerWidth / boxWidth);
33585         
33586         this.cols = Math.max( cols, 1 );
33587         
33588         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33589         
33590         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33591         
33592         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33593         
33594         this.colWidth = boxWidth + avail - this.padWidth;
33595         
33596         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33597         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33598     },
33599     
33600     horizontalMeasureColumns : function()
33601     {
33602         this.getContainerWidth();
33603         
33604         var boxWidth = this.boxWidth;
33605         
33606         if(this.containerWidth < boxWidth){
33607             boxWidth = this.containerWidth;
33608         }
33609         
33610         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33611         
33612         this.el.setHeight(boxWidth);
33613         
33614     },
33615     
33616     getContainerWidth : function()
33617     {
33618         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33619     },
33620     
33621     layoutItems : function( isInstant )
33622     {
33623         Roo.log(this.bricks);
33624         
33625         var items = Roo.apply([], this.bricks);
33626         
33627         if(this.isHorizontal){
33628             this._horizontalLayoutItems( items , isInstant );
33629             return;
33630         }
33631         
33632 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33633 //            this._verticalAlternativeLayoutItems( items , isInstant );
33634 //            return;
33635 //        }
33636         
33637         this._verticalLayoutItems( items , isInstant );
33638         
33639     },
33640     
33641     _verticalLayoutItems : function ( items , isInstant)
33642     {
33643         if ( !items || !items.length ) {
33644             return;
33645         }
33646         
33647         var standard = [
33648             ['xs', 'xs', 'xs', 'tall'],
33649             ['xs', 'xs', 'tall'],
33650             ['xs', 'xs', 'sm'],
33651             ['xs', 'xs', 'xs'],
33652             ['xs', 'tall'],
33653             ['xs', 'sm'],
33654             ['xs', 'xs'],
33655             ['xs'],
33656             
33657             ['sm', 'xs', 'xs'],
33658             ['sm', 'xs'],
33659             ['sm'],
33660             
33661             ['tall', 'xs', 'xs', 'xs'],
33662             ['tall', 'xs', 'xs'],
33663             ['tall', 'xs'],
33664             ['tall']
33665             
33666         ];
33667         
33668         var queue = [];
33669         
33670         var boxes = [];
33671         
33672         var box = [];
33673         
33674         Roo.each(items, function(item, k){
33675             
33676             switch (item.size) {
33677                 // these layouts take up a full box,
33678                 case 'md' :
33679                 case 'md-left' :
33680                 case 'md-right' :
33681                 case 'wide' :
33682                     
33683                     if(box.length){
33684                         boxes.push(box);
33685                         box = [];
33686                     }
33687                     
33688                     boxes.push([item]);
33689                     
33690                     break;
33691                     
33692                 case 'xs' :
33693                 case 'sm' :
33694                 case 'tall' :
33695                     
33696                     box.push(item);
33697                     
33698                     break;
33699                 default :
33700                     break;
33701                     
33702             }
33703             
33704         }, this);
33705         
33706         if(box.length){
33707             boxes.push(box);
33708             box = [];
33709         }
33710         
33711         var filterPattern = function(box, length)
33712         {
33713             if(!box.length){
33714                 return;
33715             }
33716             
33717             var match = false;
33718             
33719             var pattern = box.slice(0, length);
33720             
33721             var format = [];
33722             
33723             Roo.each(pattern, function(i){
33724                 format.push(i.size);
33725             }, this);
33726             
33727             Roo.each(standard, function(s){
33728                 
33729                 if(String(s) != String(format)){
33730                     return;
33731                 }
33732                 
33733                 match = true;
33734                 return false;
33735                 
33736             }, this);
33737             
33738             if(!match && length == 1){
33739                 return;
33740             }
33741             
33742             if(!match){
33743                 filterPattern(box, length - 1);
33744                 return;
33745             }
33746                 
33747             queue.push(pattern);
33748
33749             box = box.slice(length, box.length);
33750
33751             filterPattern(box, 4);
33752
33753             return;
33754             
33755         }
33756         
33757         Roo.each(boxes, function(box, k){
33758             
33759             if(!box.length){
33760                 return;
33761             }
33762             
33763             if(box.length == 1){
33764                 queue.push(box);
33765                 return;
33766             }
33767             
33768             filterPattern(box, 4);
33769             
33770         }, this);
33771         
33772         this._processVerticalLayoutQueue( queue, isInstant );
33773         
33774     },
33775     
33776 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33777 //    {
33778 //        if ( !items || !items.length ) {
33779 //            return;
33780 //        }
33781 //
33782 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33783 //        
33784 //    },
33785     
33786     _horizontalLayoutItems : function ( items , isInstant)
33787     {
33788         if ( !items || !items.length || items.length < 3) {
33789             return;
33790         }
33791         
33792         items.reverse();
33793         
33794         var eItems = items.slice(0, 3);
33795         
33796         items = items.slice(3, items.length);
33797         
33798         var standard = [
33799             ['xs', 'xs', 'xs', 'wide'],
33800             ['xs', 'xs', 'wide'],
33801             ['xs', 'xs', 'sm'],
33802             ['xs', 'xs', 'xs'],
33803             ['xs', 'wide'],
33804             ['xs', 'sm'],
33805             ['xs', 'xs'],
33806             ['xs'],
33807             
33808             ['sm', 'xs', 'xs'],
33809             ['sm', 'xs'],
33810             ['sm'],
33811             
33812             ['wide', 'xs', 'xs', 'xs'],
33813             ['wide', 'xs', 'xs'],
33814             ['wide', 'xs'],
33815             ['wide'],
33816             
33817             ['wide-thin']
33818         ];
33819         
33820         var queue = [];
33821         
33822         var boxes = [];
33823         
33824         var box = [];
33825         
33826         Roo.each(items, function(item, k){
33827             
33828             switch (item.size) {
33829                 case 'md' :
33830                 case 'md-left' :
33831                 case 'md-right' :
33832                 case 'tall' :
33833                     
33834                     if(box.length){
33835                         boxes.push(box);
33836                         box = [];
33837                     }
33838                     
33839                     boxes.push([item]);
33840                     
33841                     break;
33842                     
33843                 case 'xs' :
33844                 case 'sm' :
33845                 case 'wide' :
33846                 case 'wide-thin' :
33847                     
33848                     box.push(item);
33849                     
33850                     break;
33851                 default :
33852                     break;
33853                     
33854             }
33855             
33856         }, this);
33857         
33858         if(box.length){
33859             boxes.push(box);
33860             box = [];
33861         }
33862         
33863         var filterPattern = function(box, length)
33864         {
33865             if(!box.length){
33866                 return;
33867             }
33868             
33869             var match = false;
33870             
33871             var pattern = box.slice(0, length);
33872             
33873             var format = [];
33874             
33875             Roo.each(pattern, function(i){
33876                 format.push(i.size);
33877             }, this);
33878             
33879             Roo.each(standard, function(s){
33880                 
33881                 if(String(s) != String(format)){
33882                     return;
33883                 }
33884                 
33885                 match = true;
33886                 return false;
33887                 
33888             }, this);
33889             
33890             if(!match && length == 1){
33891                 return;
33892             }
33893             
33894             if(!match){
33895                 filterPattern(box, length - 1);
33896                 return;
33897             }
33898                 
33899             queue.push(pattern);
33900
33901             box = box.slice(length, box.length);
33902
33903             filterPattern(box, 4);
33904
33905             return;
33906             
33907         }
33908         
33909         Roo.each(boxes, function(box, k){
33910             
33911             if(!box.length){
33912                 return;
33913             }
33914             
33915             if(box.length == 1){
33916                 queue.push(box);
33917                 return;
33918             }
33919             
33920             filterPattern(box, 4);
33921             
33922         }, this);
33923         
33924         
33925         var prune = [];
33926         
33927         var pos = this.el.getBox(true);
33928         
33929         var minX = pos.x;
33930         
33931         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33932         
33933         var hit_end = false;
33934         
33935         Roo.each(queue, function(box){
33936             
33937             if(hit_end){
33938                 
33939                 Roo.each(box, function(b){
33940                 
33941                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33942                     b.el.hide();
33943
33944                 }, this);
33945
33946                 return;
33947             }
33948             
33949             var mx = 0;
33950             
33951             Roo.each(box, function(b){
33952                 
33953                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33954                 b.el.show();
33955
33956                 mx = Math.max(mx, b.x);
33957                 
33958             }, this);
33959             
33960             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33961             
33962             if(maxX < minX){
33963                 
33964                 Roo.each(box, function(b){
33965                 
33966                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33967                     b.el.hide();
33968                     
33969                 }, this);
33970                 
33971                 hit_end = true;
33972                 
33973                 return;
33974             }
33975             
33976             prune.push(box);
33977             
33978         }, this);
33979         
33980         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33981     },
33982     
33983     /** Sets position of item in DOM
33984     * @param {Element} item
33985     * @param {Number} x - horizontal position
33986     * @param {Number} y - vertical position
33987     * @param {Boolean} isInstant - disables transitions
33988     */
33989     _processVerticalLayoutQueue : function( queue, isInstant )
33990     {
33991         var pos = this.el.getBox(true);
33992         var x = pos.x;
33993         var y = pos.y;
33994         var maxY = [];
33995         
33996         for (var i = 0; i < this.cols; i++){
33997             maxY[i] = pos.y;
33998         }
33999         
34000         Roo.each(queue, function(box, k){
34001             
34002             var col = k % this.cols;
34003             
34004             Roo.each(box, function(b,kk){
34005                 
34006                 b.el.position('absolute');
34007                 
34008                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34009                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34010                 
34011                 if(b.size == 'md-left' || b.size == 'md-right'){
34012                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34013                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34014                 }
34015                 
34016                 b.el.setWidth(width);
34017                 b.el.setHeight(height);
34018                 // iframe?
34019                 b.el.select('iframe',true).setSize(width,height);
34020                 
34021             }, this);
34022             
34023             for (var i = 0; i < this.cols; i++){
34024                 
34025                 if(maxY[i] < maxY[col]){
34026                     col = i;
34027                     continue;
34028                 }
34029                 
34030                 col = Math.min(col, i);
34031                 
34032             }
34033             
34034             x = pos.x + col * (this.colWidth + this.padWidth);
34035             
34036             y = maxY[col];
34037             
34038             var positions = [];
34039             
34040             switch (box.length){
34041                 case 1 :
34042                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34043                     break;
34044                 case 2 :
34045                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34046                     break;
34047                 case 3 :
34048                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34049                     break;
34050                 case 4 :
34051                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34052                     break;
34053                 default :
34054                     break;
34055             }
34056             
34057             Roo.each(box, function(b,kk){
34058                 
34059                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34060                 
34061                 var sz = b.el.getSize();
34062                 
34063                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34064                 
34065             }, this);
34066             
34067         }, this);
34068         
34069         var mY = 0;
34070         
34071         for (var i = 0; i < this.cols; i++){
34072             mY = Math.max(mY, maxY[i]);
34073         }
34074         
34075         this.el.setHeight(mY - pos.y);
34076         
34077     },
34078     
34079 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34080 //    {
34081 //        var pos = this.el.getBox(true);
34082 //        var x = pos.x;
34083 //        var y = pos.y;
34084 //        var maxX = pos.right;
34085 //        
34086 //        var maxHeight = 0;
34087 //        
34088 //        Roo.each(items, function(item, k){
34089 //            
34090 //            var c = k % 2;
34091 //            
34092 //            item.el.position('absolute');
34093 //                
34094 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34095 //
34096 //            item.el.setWidth(width);
34097 //
34098 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34099 //
34100 //            item.el.setHeight(height);
34101 //            
34102 //            if(c == 0){
34103 //                item.el.setXY([x, y], isInstant ? false : true);
34104 //            } else {
34105 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34106 //            }
34107 //            
34108 //            y = y + height + this.alternativePadWidth;
34109 //            
34110 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34111 //            
34112 //        }, this);
34113 //        
34114 //        this.el.setHeight(maxHeight);
34115 //        
34116 //    },
34117     
34118     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34119     {
34120         var pos = this.el.getBox(true);
34121         
34122         var minX = pos.x;
34123         var minY = pos.y;
34124         
34125         var maxX = pos.right;
34126         
34127         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34128         
34129         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34130         
34131         Roo.each(queue, function(box, k){
34132             
34133             Roo.each(box, function(b, kk){
34134                 
34135                 b.el.position('absolute');
34136                 
34137                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34138                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34139                 
34140                 if(b.size == 'md-left' || b.size == 'md-right'){
34141                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34142                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34143                 }
34144                 
34145                 b.el.setWidth(width);
34146                 b.el.setHeight(height);
34147                 
34148             }, this);
34149             
34150             if(!box.length){
34151                 return;
34152             }
34153             
34154             var positions = [];
34155             
34156             switch (box.length){
34157                 case 1 :
34158                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34159                     break;
34160                 case 2 :
34161                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34162                     break;
34163                 case 3 :
34164                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34165                     break;
34166                 case 4 :
34167                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34168                     break;
34169                 default :
34170                     break;
34171             }
34172             
34173             Roo.each(box, function(b,kk){
34174                 
34175                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34176                 
34177                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34178                 
34179             }, this);
34180             
34181         }, this);
34182         
34183     },
34184     
34185     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34186     {
34187         Roo.each(eItems, function(b,k){
34188             
34189             b.size = (k == 0) ? 'sm' : 'xs';
34190             b.x = (k == 0) ? 2 : 1;
34191             b.y = (k == 0) ? 2 : 1;
34192             
34193             b.el.position('absolute');
34194             
34195             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34196                 
34197             b.el.setWidth(width);
34198             
34199             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34200             
34201             b.el.setHeight(height);
34202             
34203         }, this);
34204
34205         var positions = [];
34206         
34207         positions.push({
34208             x : maxX - this.unitWidth * 2 - this.gutter,
34209             y : minY
34210         });
34211         
34212         positions.push({
34213             x : maxX - this.unitWidth,
34214             y : minY + (this.unitWidth + this.gutter) * 2
34215         });
34216         
34217         positions.push({
34218             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34219             y : minY
34220         });
34221         
34222         Roo.each(eItems, function(b,k){
34223             
34224             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34225
34226         }, this);
34227         
34228     },
34229     
34230     getVerticalOneBoxColPositions : function(x, y, box)
34231     {
34232         var pos = [];
34233         
34234         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34235         
34236         if(box[0].size == 'md-left'){
34237             rand = 0;
34238         }
34239         
34240         if(box[0].size == 'md-right'){
34241             rand = 1;
34242         }
34243         
34244         pos.push({
34245             x : x + (this.unitWidth + this.gutter) * rand,
34246             y : y
34247         });
34248         
34249         return pos;
34250     },
34251     
34252     getVerticalTwoBoxColPositions : function(x, y, box)
34253     {
34254         var pos = [];
34255         
34256         if(box[0].size == 'xs'){
34257             
34258             pos.push({
34259                 x : x,
34260                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34261             });
34262
34263             pos.push({
34264                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34265                 y : y
34266             });
34267             
34268             return pos;
34269             
34270         }
34271         
34272         pos.push({
34273             x : x,
34274             y : y
34275         });
34276
34277         pos.push({
34278             x : x + (this.unitWidth + this.gutter) * 2,
34279             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34280         });
34281         
34282         return pos;
34283         
34284     },
34285     
34286     getVerticalThreeBoxColPositions : function(x, y, box)
34287     {
34288         var pos = [];
34289         
34290         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34291             
34292             pos.push({
34293                 x : x,
34294                 y : y
34295             });
34296
34297             pos.push({
34298                 x : x + (this.unitWidth + this.gutter) * 1,
34299                 y : y
34300             });
34301             
34302             pos.push({
34303                 x : x + (this.unitWidth + this.gutter) * 2,
34304                 y : y
34305             });
34306             
34307             return pos;
34308             
34309         }
34310         
34311         if(box[0].size == 'xs' && box[1].size == 'xs'){
34312             
34313             pos.push({
34314                 x : x,
34315                 y : y
34316             });
34317
34318             pos.push({
34319                 x : x,
34320                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34321             });
34322             
34323             pos.push({
34324                 x : x + (this.unitWidth + this.gutter) * 1,
34325                 y : y
34326             });
34327             
34328             return pos;
34329             
34330         }
34331         
34332         pos.push({
34333             x : x,
34334             y : y
34335         });
34336
34337         pos.push({
34338             x : x + (this.unitWidth + this.gutter) * 2,
34339             y : y
34340         });
34341
34342         pos.push({
34343             x : x + (this.unitWidth + this.gutter) * 2,
34344             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34345         });
34346             
34347         return pos;
34348         
34349     },
34350     
34351     getVerticalFourBoxColPositions : function(x, y, box)
34352     {
34353         var pos = [];
34354         
34355         if(box[0].size == 'xs'){
34356             
34357             pos.push({
34358                 x : x,
34359                 y : y
34360             });
34361
34362             pos.push({
34363                 x : x,
34364                 y : y + (this.unitHeight + this.gutter) * 1
34365             });
34366             
34367             pos.push({
34368                 x : x,
34369                 y : y + (this.unitHeight + this.gutter) * 2
34370             });
34371             
34372             pos.push({
34373                 x : x + (this.unitWidth + this.gutter) * 1,
34374                 y : y
34375             });
34376             
34377             return pos;
34378             
34379         }
34380         
34381         pos.push({
34382             x : x,
34383             y : y
34384         });
34385
34386         pos.push({
34387             x : x + (this.unitWidth + this.gutter) * 2,
34388             y : y
34389         });
34390
34391         pos.push({
34392             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34393             y : y + (this.unitHeight + this.gutter) * 1
34394         });
34395
34396         pos.push({
34397             x : x + (this.unitWidth + this.gutter) * 2,
34398             y : y + (this.unitWidth + this.gutter) * 2
34399         });
34400
34401         return pos;
34402         
34403     },
34404     
34405     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34406     {
34407         var pos = [];
34408         
34409         if(box[0].size == 'md-left'){
34410             pos.push({
34411                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34412                 y : minY
34413             });
34414             
34415             return pos;
34416         }
34417         
34418         if(box[0].size == 'md-right'){
34419             pos.push({
34420                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34421                 y : minY + (this.unitWidth + this.gutter) * 1
34422             });
34423             
34424             return pos;
34425         }
34426         
34427         var rand = Math.floor(Math.random() * (4 - box[0].y));
34428         
34429         pos.push({
34430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34431             y : minY + (this.unitWidth + this.gutter) * rand
34432         });
34433         
34434         return pos;
34435         
34436     },
34437     
34438     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34439     {
34440         var pos = [];
34441         
34442         if(box[0].size == 'xs'){
34443             
34444             pos.push({
34445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34446                 y : minY
34447             });
34448
34449             pos.push({
34450                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34451                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34452             });
34453             
34454             return pos;
34455             
34456         }
34457         
34458         pos.push({
34459             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34460             y : minY
34461         });
34462
34463         pos.push({
34464             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34465             y : minY + (this.unitWidth + this.gutter) * 2
34466         });
34467         
34468         return pos;
34469         
34470     },
34471     
34472     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34473     {
34474         var pos = [];
34475         
34476         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34477             
34478             pos.push({
34479                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34480                 y : minY
34481             });
34482
34483             pos.push({
34484                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34485                 y : minY + (this.unitWidth + this.gutter) * 1
34486             });
34487             
34488             pos.push({
34489                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34490                 y : minY + (this.unitWidth + this.gutter) * 2
34491             });
34492             
34493             return pos;
34494             
34495         }
34496         
34497         if(box[0].size == 'xs' && box[1].size == 'xs'){
34498             
34499             pos.push({
34500                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34501                 y : minY
34502             });
34503
34504             pos.push({
34505                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34506                 y : minY
34507             });
34508             
34509             pos.push({
34510                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34511                 y : minY + (this.unitWidth + this.gutter) * 1
34512             });
34513             
34514             return pos;
34515             
34516         }
34517         
34518         pos.push({
34519             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34520             y : minY
34521         });
34522
34523         pos.push({
34524             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34525             y : minY + (this.unitWidth + this.gutter) * 2
34526         });
34527
34528         pos.push({
34529             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34530             y : minY + (this.unitWidth + this.gutter) * 2
34531         });
34532             
34533         return pos;
34534         
34535     },
34536     
34537     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34538     {
34539         var pos = [];
34540         
34541         if(box[0].size == 'xs'){
34542             
34543             pos.push({
34544                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34545                 y : minY
34546             });
34547
34548             pos.push({
34549                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34550                 y : minY
34551             });
34552             
34553             pos.push({
34554                 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),
34555                 y : minY
34556             });
34557             
34558             pos.push({
34559                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34560                 y : minY + (this.unitWidth + this.gutter) * 1
34561             });
34562             
34563             return pos;
34564             
34565         }
34566         
34567         pos.push({
34568             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34569             y : minY
34570         });
34571         
34572         pos.push({
34573             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574             y : minY + (this.unitWidth + this.gutter) * 2
34575         });
34576         
34577         pos.push({
34578             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579             y : minY + (this.unitWidth + this.gutter) * 2
34580         });
34581         
34582         pos.push({
34583             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),
34584             y : minY + (this.unitWidth + this.gutter) * 2
34585         });
34586
34587         return pos;
34588         
34589     },
34590     
34591     /**
34592     * remove a Masonry Brick
34593     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34594     */
34595     removeBrick : function(brick_id)
34596     {
34597         if (!brick_id) {
34598             return;
34599         }
34600         
34601         for (var i = 0; i<this.bricks.length; i++) {
34602             if (this.bricks[i].id == brick_id) {
34603                 this.bricks.splice(i,1);
34604                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34605                 this.initial();
34606             }
34607         }
34608     },
34609     
34610     /**
34611     * adds a Masonry Brick
34612     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34613     */
34614     addBrick : function(cfg)
34615     {
34616         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34617         //this.register(cn);
34618         cn.parentId = this.id;
34619         cn.render(this.el);
34620         return cn;
34621     },
34622     
34623     /**
34624     * register a Masonry Brick
34625     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34626     */
34627     
34628     register : function(brick)
34629     {
34630         this.bricks.push(brick);
34631         brick.masonryId = this.id;
34632     },
34633     
34634     /**
34635     * clear all the Masonry Brick
34636     */
34637     clearAll : function()
34638     {
34639         this.bricks = [];
34640         //this.getChildContainer().dom.innerHTML = "";
34641         this.el.dom.innerHTML = '';
34642     },
34643     
34644     getSelected : function()
34645     {
34646         if (!this.selectedBrick) {
34647             return false;
34648         }
34649         
34650         return this.selectedBrick;
34651     }
34652 });
34653
34654 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34655     
34656     groups: {},
34657      /**
34658     * register a Masonry Layout
34659     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34660     */
34661     
34662     register : function(layout)
34663     {
34664         this.groups[layout.id] = layout;
34665     },
34666     /**
34667     * fetch a  Masonry Layout based on the masonry layout ID
34668     * @param {string} the masonry layout to add
34669     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34670     */
34671     
34672     get: function(layout_id) {
34673         if (typeof(this.groups[layout_id]) == 'undefined') {
34674             return false;
34675         }
34676         return this.groups[layout_id] ;
34677     }
34678     
34679     
34680     
34681 });
34682
34683  
34684
34685  /**
34686  *
34687  * This is based on 
34688  * http://masonry.desandro.com
34689  *
34690  * The idea is to render all the bricks based on vertical width...
34691  *
34692  * The original code extends 'outlayer' - we might need to use that....
34693  * 
34694  */
34695
34696
34697 /**
34698  * @class Roo.bootstrap.LayoutMasonryAuto
34699  * @extends Roo.bootstrap.Component
34700  * Bootstrap Layout Masonry class
34701  * 
34702  * @constructor
34703  * Create a new Element
34704  * @param {Object} config The config object
34705  */
34706
34707 Roo.bootstrap.LayoutMasonryAuto = function(config){
34708     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34709 };
34710
34711 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34712     
34713       /**
34714      * @cfg {Boolean} isFitWidth  - resize the width..
34715      */   
34716     isFitWidth : false,  // options..
34717     /**
34718      * @cfg {Boolean} isOriginLeft = left align?
34719      */   
34720     isOriginLeft : true,
34721     /**
34722      * @cfg {Boolean} isOriginTop = top align?
34723      */   
34724     isOriginTop : false,
34725     /**
34726      * @cfg {Boolean} isLayoutInstant = no animation?
34727      */   
34728     isLayoutInstant : false, // needed?
34729     /**
34730      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34731      */   
34732     isResizingContainer : true,
34733     /**
34734      * @cfg {Number} columnWidth  width of the columns 
34735      */   
34736     
34737     columnWidth : 0,
34738     
34739     /**
34740      * @cfg {Number} maxCols maximum number of columns
34741      */   
34742     
34743     maxCols: 0,
34744     /**
34745      * @cfg {Number} padHeight padding below box..
34746      */   
34747     
34748     padHeight : 10, 
34749     
34750     /**
34751      * @cfg {Boolean} isAutoInitial defalut true
34752      */   
34753     
34754     isAutoInitial : true, 
34755     
34756     // private?
34757     gutter : 0,
34758     
34759     containerWidth: 0,
34760     initialColumnWidth : 0,
34761     currentSize : null,
34762     
34763     colYs : null, // array.
34764     maxY : 0,
34765     padWidth: 10,
34766     
34767     
34768     tag: 'div',
34769     cls: '',
34770     bricks: null, //CompositeElement
34771     cols : 0, // array?
34772     // element : null, // wrapped now this.el
34773     _isLayoutInited : null, 
34774     
34775     
34776     getAutoCreate : function(){
34777         
34778         var cfg = {
34779             tag: this.tag,
34780             cls: 'blog-masonary-wrapper ' + this.cls,
34781             cn : {
34782                 cls : 'mas-boxes masonary'
34783             }
34784         };
34785         
34786         return cfg;
34787     },
34788     
34789     getChildContainer: function( )
34790     {
34791         if (this.boxesEl) {
34792             return this.boxesEl;
34793         }
34794         
34795         this.boxesEl = this.el.select('.mas-boxes').first();
34796         
34797         return this.boxesEl;
34798     },
34799     
34800     
34801     initEvents : function()
34802     {
34803         var _this = this;
34804         
34805         if(this.isAutoInitial){
34806             Roo.log('hook children rendered');
34807             this.on('childrenrendered', function() {
34808                 Roo.log('children rendered');
34809                 _this.initial();
34810             } ,this);
34811         }
34812         
34813     },
34814     
34815     initial : function()
34816     {
34817         this.reloadItems();
34818
34819         this.currentSize = this.el.getBox(true);
34820
34821         /// was window resize... - let's see if this works..
34822         Roo.EventManager.onWindowResize(this.resize, this); 
34823
34824         if(!this.isAutoInitial){
34825             this.layout();
34826             return;
34827         }
34828         
34829         this.layout.defer(500,this);
34830     },
34831     
34832     reloadItems: function()
34833     {
34834         this.bricks = this.el.select('.masonry-brick', true);
34835         
34836         this.bricks.each(function(b) {
34837             //Roo.log(b.getSize());
34838             if (!b.attr('originalwidth')) {
34839                 b.attr('originalwidth',  b.getSize().width);
34840             }
34841             
34842         });
34843         
34844         Roo.log(this.bricks.elements.length);
34845     },
34846     
34847     resize : function()
34848     {
34849         Roo.log('resize');
34850         var cs = this.el.getBox(true);
34851         
34852         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34853             Roo.log("no change in with or X");
34854             return;
34855         }
34856         this.currentSize = cs;
34857         this.layout();
34858     },
34859     
34860     layout : function()
34861     {
34862          Roo.log('layout');
34863         this._resetLayout();
34864         //this._manageStamps();
34865       
34866         // don't animate first layout
34867         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34868         this.layoutItems( isInstant );
34869       
34870         // flag for initalized
34871         this._isLayoutInited = true;
34872     },
34873     
34874     layoutItems : function( isInstant )
34875     {
34876         //var items = this._getItemsForLayout( this.items );
34877         // original code supports filtering layout items.. we just ignore it..
34878         
34879         this._layoutItems( this.bricks , isInstant );
34880       
34881         this._postLayout();
34882     },
34883     _layoutItems : function ( items , isInstant)
34884     {
34885        //this.fireEvent( 'layout', this, items );
34886     
34887
34888         if ( !items || !items.elements.length ) {
34889           // no items, emit event with empty array
34890             return;
34891         }
34892
34893         var queue = [];
34894         items.each(function(item) {
34895             Roo.log("layout item");
34896             Roo.log(item);
34897             // get x/y object from method
34898             var position = this._getItemLayoutPosition( item );
34899             // enqueue
34900             position.item = item;
34901             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34902             queue.push( position );
34903         }, this);
34904       
34905         this._processLayoutQueue( queue );
34906     },
34907     /** Sets position of item in DOM
34908     * @param {Element} item
34909     * @param {Number} x - horizontal position
34910     * @param {Number} y - vertical position
34911     * @param {Boolean} isInstant - disables transitions
34912     */
34913     _processLayoutQueue : function( queue )
34914     {
34915         for ( var i=0, len = queue.length; i < len; i++ ) {
34916             var obj = queue[i];
34917             obj.item.position('absolute');
34918             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34919         }
34920     },
34921       
34922     
34923     /**
34924     * Any logic you want to do after each layout,
34925     * i.e. size the container
34926     */
34927     _postLayout : function()
34928     {
34929         this.resizeContainer();
34930     },
34931     
34932     resizeContainer : function()
34933     {
34934         if ( !this.isResizingContainer ) {
34935             return;
34936         }
34937         var size = this._getContainerSize();
34938         if ( size ) {
34939             this.el.setSize(size.width,size.height);
34940             this.boxesEl.setSize(size.width,size.height);
34941         }
34942     },
34943     
34944     
34945     
34946     _resetLayout : function()
34947     {
34948         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34949         this.colWidth = this.el.getWidth();
34950         //this.gutter = this.el.getWidth(); 
34951         
34952         this.measureColumns();
34953
34954         // reset column Y
34955         var i = this.cols;
34956         this.colYs = [];
34957         while (i--) {
34958             this.colYs.push( 0 );
34959         }
34960     
34961         this.maxY = 0;
34962     },
34963
34964     measureColumns : function()
34965     {
34966         this.getContainerWidth();
34967       // if columnWidth is 0, default to outerWidth of first item
34968         if ( !this.columnWidth ) {
34969             var firstItem = this.bricks.first();
34970             Roo.log(firstItem);
34971             this.columnWidth  = this.containerWidth;
34972             if (firstItem && firstItem.attr('originalwidth') ) {
34973                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34974             }
34975             // columnWidth fall back to item of first element
34976             Roo.log("set column width?");
34977                         this.initialColumnWidth = this.columnWidth  ;
34978
34979             // if first elem has no width, default to size of container
34980             
34981         }
34982         
34983         
34984         if (this.initialColumnWidth) {
34985             this.columnWidth = this.initialColumnWidth;
34986         }
34987         
34988         
34989             
34990         // column width is fixed at the top - however if container width get's smaller we should
34991         // reduce it...
34992         
34993         // this bit calcs how man columns..
34994             
34995         var columnWidth = this.columnWidth += this.gutter;
34996       
34997         // calculate columns
34998         var containerWidth = this.containerWidth + this.gutter;
34999         
35000         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35001         // fix rounding errors, typically with gutters
35002         var excess = columnWidth - containerWidth % columnWidth;
35003         
35004         
35005         // if overshoot is less than a pixel, round up, otherwise floor it
35006         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35007         cols = Math[ mathMethod ]( cols );
35008         this.cols = Math.max( cols, 1 );
35009         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35010         
35011          // padding positioning..
35012         var totalColWidth = this.cols * this.columnWidth;
35013         var padavail = this.containerWidth - totalColWidth;
35014         // so for 2 columns - we need 3 'pads'
35015         
35016         var padNeeded = (1+this.cols) * this.padWidth;
35017         
35018         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35019         
35020         this.columnWidth += padExtra
35021         //this.padWidth = Math.floor(padavail /  ( this.cols));
35022         
35023         // adjust colum width so that padding is fixed??
35024         
35025         // we have 3 columns ... total = width * 3
35026         // we have X left over... that should be used by 
35027         
35028         //if (this.expandC) {
35029             
35030         //}
35031         
35032         
35033         
35034     },
35035     
35036     getContainerWidth : function()
35037     {
35038        /* // container is parent if fit width
35039         var container = this.isFitWidth ? this.element.parentNode : this.element;
35040         // check that this.size and size are there
35041         // IE8 triggers resize on body size change, so they might not be
35042         
35043         var size = getSize( container );  //FIXME
35044         this.containerWidth = size && size.innerWidth; //FIXME
35045         */
35046          
35047         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35048         
35049     },
35050     
35051     _getItemLayoutPosition : function( item )  // what is item?
35052     {
35053         // we resize the item to our columnWidth..
35054       
35055         item.setWidth(this.columnWidth);
35056         item.autoBoxAdjust  = false;
35057         
35058         var sz = item.getSize();
35059  
35060         // how many columns does this brick span
35061         var remainder = this.containerWidth % this.columnWidth;
35062         
35063         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35064         // round if off by 1 pixel, otherwise use ceil
35065         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35066         colSpan = Math.min( colSpan, this.cols );
35067         
35068         // normally this should be '1' as we dont' currently allow multi width columns..
35069         
35070         var colGroup = this._getColGroup( colSpan );
35071         // get the minimum Y value from the columns
35072         var minimumY = Math.min.apply( Math, colGroup );
35073         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35074         
35075         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35076          
35077         // position the brick
35078         var position = {
35079             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35080             y: this.currentSize.y + minimumY + this.padHeight
35081         };
35082         
35083         Roo.log(position);
35084         // apply setHeight to necessary columns
35085         var setHeight = minimumY + sz.height + this.padHeight;
35086         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35087         
35088         var setSpan = this.cols + 1 - colGroup.length;
35089         for ( var i = 0; i < setSpan; i++ ) {
35090           this.colYs[ shortColIndex + i ] = setHeight ;
35091         }
35092       
35093         return position;
35094     },
35095     
35096     /**
35097      * @param {Number} colSpan - number of columns the element spans
35098      * @returns {Array} colGroup
35099      */
35100     _getColGroup : function( colSpan )
35101     {
35102         if ( colSpan < 2 ) {
35103           // if brick spans only one column, use all the column Ys
35104           return this.colYs;
35105         }
35106       
35107         var colGroup = [];
35108         // how many different places could this brick fit horizontally
35109         var groupCount = this.cols + 1 - colSpan;
35110         // for each group potential horizontal position
35111         for ( var i = 0; i < groupCount; i++ ) {
35112           // make an array of colY values for that one group
35113           var groupColYs = this.colYs.slice( i, i + colSpan );
35114           // and get the max value of the array
35115           colGroup[i] = Math.max.apply( Math, groupColYs );
35116         }
35117         return colGroup;
35118     },
35119     /*
35120     _manageStamp : function( stamp )
35121     {
35122         var stampSize =  stamp.getSize();
35123         var offset = stamp.getBox();
35124         // get the columns that this stamp affects
35125         var firstX = this.isOriginLeft ? offset.x : offset.right;
35126         var lastX = firstX + stampSize.width;
35127         var firstCol = Math.floor( firstX / this.columnWidth );
35128         firstCol = Math.max( 0, firstCol );
35129         
35130         var lastCol = Math.floor( lastX / this.columnWidth );
35131         // lastCol should not go over if multiple of columnWidth #425
35132         lastCol -= lastX % this.columnWidth ? 0 : 1;
35133         lastCol = Math.min( this.cols - 1, lastCol );
35134         
35135         // set colYs to bottom of the stamp
35136         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35137             stampSize.height;
35138             
35139         for ( var i = firstCol; i <= lastCol; i++ ) {
35140           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35141         }
35142     },
35143     */
35144     
35145     _getContainerSize : function()
35146     {
35147         this.maxY = Math.max.apply( Math, this.colYs );
35148         var size = {
35149             height: this.maxY
35150         };
35151       
35152         if ( this.isFitWidth ) {
35153             size.width = this._getContainerFitWidth();
35154         }
35155       
35156         return size;
35157     },
35158     
35159     _getContainerFitWidth : function()
35160     {
35161         var unusedCols = 0;
35162         // count unused columns
35163         var i = this.cols;
35164         while ( --i ) {
35165           if ( this.colYs[i] !== 0 ) {
35166             break;
35167           }
35168           unusedCols++;
35169         }
35170         // fit container to columns that have been used
35171         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35172     },
35173     
35174     needsResizeLayout : function()
35175     {
35176         var previousWidth = this.containerWidth;
35177         this.getContainerWidth();
35178         return previousWidth !== this.containerWidth;
35179     }
35180  
35181 });
35182
35183  
35184
35185  /*
35186  * - LGPL
35187  *
35188  * element
35189  * 
35190  */
35191
35192 /**
35193  * @class Roo.bootstrap.MasonryBrick
35194  * @extends Roo.bootstrap.Component
35195  * Bootstrap MasonryBrick class
35196  * 
35197  * @constructor
35198  * Create a new MasonryBrick
35199  * @param {Object} config The config object
35200  */
35201
35202 Roo.bootstrap.MasonryBrick = function(config){
35203     
35204     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35205     
35206     Roo.bootstrap.MasonryBrick.register(this);
35207     
35208     this.addEvents({
35209         // raw events
35210         /**
35211          * @event click
35212          * When a MasonryBrick is clcik
35213          * @param {Roo.bootstrap.MasonryBrick} this
35214          * @param {Roo.EventObject} e
35215          */
35216         "click" : true
35217     });
35218 };
35219
35220 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35221     
35222     /**
35223      * @cfg {String} title
35224      */   
35225     title : '',
35226     /**
35227      * @cfg {String} html
35228      */   
35229     html : '',
35230     /**
35231      * @cfg {String} bgimage
35232      */   
35233     bgimage : '',
35234     /**
35235      * @cfg {String} videourl
35236      */   
35237     videourl : '',
35238     /**
35239      * @cfg {String} cls
35240      */   
35241     cls : '',
35242     /**
35243      * @cfg {String} href
35244      */   
35245     href : '',
35246     /**
35247      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35248      */   
35249     size : 'xs',
35250     
35251     /**
35252      * @cfg {String} placetitle (center|bottom)
35253      */   
35254     placetitle : '',
35255     
35256     /**
35257      * @cfg {Boolean} isFitContainer defalut true
35258      */   
35259     isFitContainer : true, 
35260     
35261     /**
35262      * @cfg {Boolean} preventDefault defalut false
35263      */   
35264     preventDefault : false, 
35265     
35266     /**
35267      * @cfg {Boolean} inverse defalut false
35268      */   
35269     maskInverse : false, 
35270     
35271     getAutoCreate : function()
35272     {
35273         if(!this.isFitContainer){
35274             return this.getSplitAutoCreate();
35275         }
35276         
35277         var cls = 'masonry-brick masonry-brick-full';
35278         
35279         if(this.href.length){
35280             cls += ' masonry-brick-link';
35281         }
35282         
35283         if(this.bgimage.length){
35284             cls += ' masonry-brick-image';
35285         }
35286         
35287         if(this.maskInverse){
35288             cls += ' mask-inverse';
35289         }
35290         
35291         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35292             cls += ' enable-mask';
35293         }
35294         
35295         if(this.size){
35296             cls += ' masonry-' + this.size + '-brick';
35297         }
35298         
35299         if(this.placetitle.length){
35300             
35301             switch (this.placetitle) {
35302                 case 'center' :
35303                     cls += ' masonry-center-title';
35304                     break;
35305                 case 'bottom' :
35306                     cls += ' masonry-bottom-title';
35307                     break;
35308                 default:
35309                     break;
35310             }
35311             
35312         } else {
35313             if(!this.html.length && !this.bgimage.length){
35314                 cls += ' masonry-center-title';
35315             }
35316
35317             if(!this.html.length && this.bgimage.length){
35318                 cls += ' masonry-bottom-title';
35319             }
35320         }
35321         
35322         if(this.cls){
35323             cls += ' ' + this.cls;
35324         }
35325         
35326         var cfg = {
35327             tag: (this.href.length) ? 'a' : 'div',
35328             cls: cls,
35329             cn: [
35330                 {
35331                     tag: 'div',
35332                     cls: 'masonry-brick-mask'
35333                 },
35334                 {
35335                     tag: 'div',
35336                     cls: 'masonry-brick-paragraph',
35337                     cn: []
35338                 }
35339             ]
35340         };
35341         
35342         if(this.href.length){
35343             cfg.href = this.href;
35344         }
35345         
35346         var cn = cfg.cn[1].cn;
35347         
35348         if(this.title.length){
35349             cn.push({
35350                 tag: 'h4',
35351                 cls: 'masonry-brick-title',
35352                 html: this.title
35353             });
35354         }
35355         
35356         if(this.html.length){
35357             cn.push({
35358                 tag: 'p',
35359                 cls: 'masonry-brick-text',
35360                 html: this.html
35361             });
35362         }
35363         
35364         if (!this.title.length && !this.html.length) {
35365             cfg.cn[1].cls += ' hide';
35366         }
35367         
35368         if(this.bgimage.length){
35369             cfg.cn.push({
35370                 tag: 'img',
35371                 cls: 'masonry-brick-image-view',
35372                 src: this.bgimage
35373             });
35374         }
35375         
35376         if(this.videourl.length){
35377             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35378             // youtube support only?
35379             cfg.cn.push({
35380                 tag: 'iframe',
35381                 cls: 'masonry-brick-image-view',
35382                 src: vurl,
35383                 frameborder : 0,
35384                 allowfullscreen : true
35385             });
35386         }
35387         
35388         return cfg;
35389         
35390     },
35391     
35392     getSplitAutoCreate : function()
35393     {
35394         var cls = 'masonry-brick masonry-brick-split';
35395         
35396         if(this.href.length){
35397             cls += ' masonry-brick-link';
35398         }
35399         
35400         if(this.bgimage.length){
35401             cls += ' masonry-brick-image';
35402         }
35403         
35404         if(this.size){
35405             cls += ' masonry-' + this.size + '-brick';
35406         }
35407         
35408         switch (this.placetitle) {
35409             case 'center' :
35410                 cls += ' masonry-center-title';
35411                 break;
35412             case 'bottom' :
35413                 cls += ' masonry-bottom-title';
35414                 break;
35415             default:
35416                 if(!this.bgimage.length){
35417                     cls += ' masonry-center-title';
35418                 }
35419
35420                 if(this.bgimage.length){
35421                     cls += ' masonry-bottom-title';
35422                 }
35423                 break;
35424         }
35425         
35426         if(this.cls){
35427             cls += ' ' + this.cls;
35428         }
35429         
35430         var cfg = {
35431             tag: (this.href.length) ? 'a' : 'div',
35432             cls: cls,
35433             cn: [
35434                 {
35435                     tag: 'div',
35436                     cls: 'masonry-brick-split-head',
35437                     cn: [
35438                         {
35439                             tag: 'div',
35440                             cls: 'masonry-brick-paragraph',
35441                             cn: []
35442                         }
35443                     ]
35444                 },
35445                 {
35446                     tag: 'div',
35447                     cls: 'masonry-brick-split-body',
35448                     cn: []
35449                 }
35450             ]
35451         };
35452         
35453         if(this.href.length){
35454             cfg.href = this.href;
35455         }
35456         
35457         if(this.title.length){
35458             cfg.cn[0].cn[0].cn.push({
35459                 tag: 'h4',
35460                 cls: 'masonry-brick-title',
35461                 html: this.title
35462             });
35463         }
35464         
35465         if(this.html.length){
35466             cfg.cn[1].cn.push({
35467                 tag: 'p',
35468                 cls: 'masonry-brick-text',
35469                 html: this.html
35470             });
35471         }
35472
35473         if(this.bgimage.length){
35474             cfg.cn[0].cn.push({
35475                 tag: 'img',
35476                 cls: 'masonry-brick-image-view',
35477                 src: this.bgimage
35478             });
35479         }
35480         
35481         if(this.videourl.length){
35482             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35483             // youtube support only?
35484             cfg.cn[0].cn.cn.push({
35485                 tag: 'iframe',
35486                 cls: 'masonry-brick-image-view',
35487                 src: vurl,
35488                 frameborder : 0,
35489                 allowfullscreen : true
35490             });
35491         }
35492         
35493         return cfg;
35494     },
35495     
35496     initEvents: function() 
35497     {
35498         switch (this.size) {
35499             case 'xs' :
35500                 this.x = 1;
35501                 this.y = 1;
35502                 break;
35503             case 'sm' :
35504                 this.x = 2;
35505                 this.y = 2;
35506                 break;
35507             case 'md' :
35508             case 'md-left' :
35509             case 'md-right' :
35510                 this.x = 3;
35511                 this.y = 3;
35512                 break;
35513             case 'tall' :
35514                 this.x = 2;
35515                 this.y = 3;
35516                 break;
35517             case 'wide' :
35518                 this.x = 3;
35519                 this.y = 2;
35520                 break;
35521             case 'wide-thin' :
35522                 this.x = 3;
35523                 this.y = 1;
35524                 break;
35525                         
35526             default :
35527                 break;
35528         }
35529         
35530         if(Roo.isTouch){
35531             this.el.on('touchstart', this.onTouchStart, this);
35532             this.el.on('touchmove', this.onTouchMove, this);
35533             this.el.on('touchend', this.onTouchEnd, this);
35534             this.el.on('contextmenu', this.onContextMenu, this);
35535         } else {
35536             this.el.on('mouseenter'  ,this.enter, this);
35537             this.el.on('mouseleave', this.leave, this);
35538             this.el.on('click', this.onClick, this);
35539         }
35540         
35541         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35542             this.parent().bricks.push(this);   
35543         }
35544         
35545     },
35546     
35547     onClick: function(e, el)
35548     {
35549         var time = this.endTimer - this.startTimer;
35550         // Roo.log(e.preventDefault());
35551         if(Roo.isTouch){
35552             if(time > 1000){
35553                 e.preventDefault();
35554                 return;
35555             }
35556         }
35557         
35558         if(!this.preventDefault){
35559             return;
35560         }
35561         
35562         e.preventDefault();
35563         
35564         if (this.activeClass != '') {
35565             this.selectBrick();
35566         }
35567         
35568         this.fireEvent('click', this, e);
35569     },
35570     
35571     enter: function(e, el)
35572     {
35573         e.preventDefault();
35574         
35575         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35576             return;
35577         }
35578         
35579         if(this.bgimage.length && this.html.length){
35580             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35581         }
35582     },
35583     
35584     leave: function(e, el)
35585     {
35586         e.preventDefault();
35587         
35588         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35589             return;
35590         }
35591         
35592         if(this.bgimage.length && this.html.length){
35593             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35594         }
35595     },
35596     
35597     onTouchStart: function(e, el)
35598     {
35599 //        e.preventDefault();
35600         
35601         this.touchmoved = false;
35602         
35603         if(!this.isFitContainer){
35604             return;
35605         }
35606         
35607         if(!this.bgimage.length || !this.html.length){
35608             return;
35609         }
35610         
35611         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35612         
35613         this.timer = new Date().getTime();
35614         
35615     },
35616     
35617     onTouchMove: function(e, el)
35618     {
35619         this.touchmoved = true;
35620     },
35621     
35622     onContextMenu : function(e,el)
35623     {
35624         e.preventDefault();
35625         e.stopPropagation();
35626         return false;
35627     },
35628     
35629     onTouchEnd: function(e, el)
35630     {
35631 //        e.preventDefault();
35632         
35633         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35634         
35635             this.leave(e,el);
35636             
35637             return;
35638         }
35639         
35640         if(!this.bgimage.length || !this.html.length){
35641             
35642             if(this.href.length){
35643                 window.location.href = this.href;
35644             }
35645             
35646             return;
35647         }
35648         
35649         if(!this.isFitContainer){
35650             return;
35651         }
35652         
35653         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35654         
35655         window.location.href = this.href;
35656     },
35657     
35658     //selection on single brick only
35659     selectBrick : function() {
35660         
35661         if (!this.parentId) {
35662             return;
35663         }
35664         
35665         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35666         var index = m.selectedBrick.indexOf(this.id);
35667         
35668         if ( index > -1) {
35669             m.selectedBrick.splice(index,1);
35670             this.el.removeClass(this.activeClass);
35671             return;
35672         }
35673         
35674         for(var i = 0; i < m.selectedBrick.length; i++) {
35675             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35676             b.el.removeClass(b.activeClass);
35677         }
35678         
35679         m.selectedBrick = [];
35680         
35681         m.selectedBrick.push(this.id);
35682         this.el.addClass(this.activeClass);
35683         return;
35684     },
35685     
35686     isSelected : function(){
35687         return this.el.hasClass(this.activeClass);
35688         
35689     }
35690 });
35691
35692 Roo.apply(Roo.bootstrap.MasonryBrick, {
35693     
35694     //groups: {},
35695     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35696      /**
35697     * register a Masonry Brick
35698     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35699     */
35700     
35701     register : function(brick)
35702     {
35703         //this.groups[brick.id] = brick;
35704         this.groups.add(brick.id, brick);
35705     },
35706     /**
35707     * fetch a  masonry brick based on the masonry brick ID
35708     * @param {string} the masonry brick to add
35709     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35710     */
35711     
35712     get: function(brick_id) 
35713     {
35714         // if (typeof(this.groups[brick_id]) == 'undefined') {
35715         //     return false;
35716         // }
35717         // return this.groups[brick_id] ;
35718         
35719         if(this.groups.key(brick_id)) {
35720             return this.groups.key(brick_id);
35721         }
35722         
35723         return false;
35724     }
35725     
35726     
35727     
35728 });
35729
35730  /*
35731  * - LGPL
35732  *
35733  * element
35734  * 
35735  */
35736
35737 /**
35738  * @class Roo.bootstrap.Brick
35739  * @extends Roo.bootstrap.Component
35740  * Bootstrap Brick class
35741  * 
35742  * @constructor
35743  * Create a new Brick
35744  * @param {Object} config The config object
35745  */
35746
35747 Roo.bootstrap.Brick = function(config){
35748     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35749     
35750     this.addEvents({
35751         // raw events
35752         /**
35753          * @event click
35754          * When a Brick is click
35755          * @param {Roo.bootstrap.Brick} this
35756          * @param {Roo.EventObject} e
35757          */
35758         "click" : true
35759     });
35760 };
35761
35762 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35763     
35764     /**
35765      * @cfg {String} title
35766      */   
35767     title : '',
35768     /**
35769      * @cfg {String} html
35770      */   
35771     html : '',
35772     /**
35773      * @cfg {String} bgimage
35774      */   
35775     bgimage : '',
35776     /**
35777      * @cfg {String} cls
35778      */   
35779     cls : '',
35780     /**
35781      * @cfg {String} href
35782      */   
35783     href : '',
35784     /**
35785      * @cfg {String} video
35786      */   
35787     video : '',
35788     /**
35789      * @cfg {Boolean} square
35790      */   
35791     square : true,
35792     
35793     getAutoCreate : function()
35794     {
35795         var cls = 'roo-brick';
35796         
35797         if(this.href.length){
35798             cls += ' roo-brick-link';
35799         }
35800         
35801         if(this.bgimage.length){
35802             cls += ' roo-brick-image';
35803         }
35804         
35805         if(!this.html.length && !this.bgimage.length){
35806             cls += ' roo-brick-center-title';
35807         }
35808         
35809         if(!this.html.length && this.bgimage.length){
35810             cls += ' roo-brick-bottom-title';
35811         }
35812         
35813         if(this.cls){
35814             cls += ' ' + this.cls;
35815         }
35816         
35817         var cfg = {
35818             tag: (this.href.length) ? 'a' : 'div',
35819             cls: cls,
35820             cn: [
35821                 {
35822                     tag: 'div',
35823                     cls: 'roo-brick-paragraph',
35824                     cn: []
35825                 }
35826             ]
35827         };
35828         
35829         if(this.href.length){
35830             cfg.href = this.href;
35831         }
35832         
35833         var cn = cfg.cn[0].cn;
35834         
35835         if(this.title.length){
35836             cn.push({
35837                 tag: 'h4',
35838                 cls: 'roo-brick-title',
35839                 html: this.title
35840             });
35841         }
35842         
35843         if(this.html.length){
35844             cn.push({
35845                 tag: 'p',
35846                 cls: 'roo-brick-text',
35847                 html: this.html
35848             });
35849         } else {
35850             cn.cls += ' hide';
35851         }
35852         
35853         if(this.bgimage.length){
35854             cfg.cn.push({
35855                 tag: 'img',
35856                 cls: 'roo-brick-image-view',
35857                 src: this.bgimage
35858             });
35859         }
35860         
35861         return cfg;
35862     },
35863     
35864     initEvents: function() 
35865     {
35866         if(this.title.length || this.html.length){
35867             this.el.on('mouseenter'  ,this.enter, this);
35868             this.el.on('mouseleave', this.leave, this);
35869         }
35870         
35871         Roo.EventManager.onWindowResize(this.resize, this); 
35872         
35873         if(this.bgimage.length){
35874             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35875             this.imageEl.on('load', this.onImageLoad, this);
35876             return;
35877         }
35878         
35879         this.resize();
35880     },
35881     
35882     onImageLoad : function()
35883     {
35884         this.resize();
35885     },
35886     
35887     resize : function()
35888     {
35889         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35890         
35891         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35892         
35893         if(this.bgimage.length){
35894             var image = this.el.select('.roo-brick-image-view', true).first();
35895             
35896             image.setWidth(paragraph.getWidth());
35897             
35898             if(this.square){
35899                 image.setHeight(paragraph.getWidth());
35900             }
35901             
35902             this.el.setHeight(image.getHeight());
35903             paragraph.setHeight(image.getHeight());
35904             
35905         }
35906         
35907     },
35908     
35909     enter: function(e, el)
35910     {
35911         e.preventDefault();
35912         
35913         if(this.bgimage.length){
35914             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35915             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35916         }
35917     },
35918     
35919     leave: function(e, el)
35920     {
35921         e.preventDefault();
35922         
35923         if(this.bgimage.length){
35924             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35925             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35926         }
35927     }
35928     
35929 });
35930
35931  
35932
35933  /*
35934  * - LGPL
35935  *
35936  * Number field 
35937  */
35938
35939 /**
35940  * @class Roo.bootstrap.NumberField
35941  * @extends Roo.bootstrap.Input
35942  * Bootstrap NumberField class
35943  * 
35944  * 
35945  * 
35946  * 
35947  * @constructor
35948  * Create a new NumberField
35949  * @param {Object} config The config object
35950  */
35951
35952 Roo.bootstrap.NumberField = function(config){
35953     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35954 };
35955
35956 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35957     
35958     /**
35959      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35960      */
35961     allowDecimals : true,
35962     /**
35963      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35964      */
35965     decimalSeparator : ".",
35966     /**
35967      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35968      */
35969     decimalPrecision : 2,
35970     /**
35971      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35972      */
35973     allowNegative : true,
35974     
35975     /**
35976      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35977      */
35978     allowZero: true,
35979     /**
35980      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35981      */
35982     minValue : Number.NEGATIVE_INFINITY,
35983     /**
35984      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35985      */
35986     maxValue : Number.MAX_VALUE,
35987     /**
35988      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35989      */
35990     minText : "The minimum value for this field is {0}",
35991     /**
35992      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35993      */
35994     maxText : "The maximum value for this field is {0}",
35995     /**
35996      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35997      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35998      */
35999     nanText : "{0} is not a valid number",
36000     /**
36001      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36002      */
36003     thousandsDelimiter : false,
36004     /**
36005      * @cfg {String} valueAlign alignment of value
36006      */
36007     valueAlign : "left",
36008
36009     getAutoCreate : function()
36010     {
36011         var hiddenInput = {
36012             tag: 'input',
36013             type: 'hidden',
36014             id: Roo.id(),
36015             cls: 'hidden-number-input'
36016         };
36017         
36018         if (this.name) {
36019             hiddenInput.name = this.name;
36020         }
36021         
36022         this.name = '';
36023         
36024         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36025         
36026         this.name = hiddenInput.name;
36027         
36028         if(cfg.cn.length > 0) {
36029             cfg.cn.push(hiddenInput);
36030         }
36031         
36032         return cfg;
36033     },
36034
36035     // private
36036     initEvents : function()
36037     {   
36038         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36039         
36040         var allowed = "0123456789";
36041         
36042         if(this.allowDecimals){
36043             allowed += this.decimalSeparator;
36044         }
36045         
36046         if(this.allowNegative){
36047             allowed += "-";
36048         }
36049         
36050         if(this.thousandsDelimiter) {
36051             allowed += ",";
36052         }
36053         
36054         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36055         
36056         var keyPress = function(e){
36057             
36058             var k = e.getKey();
36059             
36060             var c = e.getCharCode();
36061             
36062             if(
36063                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36064                     allowed.indexOf(String.fromCharCode(c)) === -1
36065             ){
36066                 e.stopEvent();
36067                 return;
36068             }
36069             
36070             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36071                 return;
36072             }
36073             
36074             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36075                 e.stopEvent();
36076             }
36077         };
36078         
36079         this.el.on("keypress", keyPress, this);
36080     },
36081     
36082     validateValue : function(value)
36083     {
36084         
36085         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36086             return false;
36087         }
36088         
36089         var num = this.parseValue(value);
36090         
36091         if(isNaN(num)){
36092             this.markInvalid(String.format(this.nanText, value));
36093             return false;
36094         }
36095         
36096         if(num < this.minValue){
36097             this.markInvalid(String.format(this.minText, this.minValue));
36098             return false;
36099         }
36100         
36101         if(num > this.maxValue){
36102             this.markInvalid(String.format(this.maxText, this.maxValue));
36103             return false;
36104         }
36105         
36106         return true;
36107     },
36108
36109     getValue : function()
36110     {
36111         var v = this.hiddenEl().getValue();
36112         
36113         return this.fixPrecision(this.parseValue(v));
36114     },
36115
36116     parseValue : function(value)
36117     {
36118         if(this.thousandsDelimiter) {
36119             value += "";
36120             r = new RegExp(",", "g");
36121             value = value.replace(r, "");
36122         }
36123         
36124         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36125         return isNaN(value) ? '' : value;
36126     },
36127
36128     fixPrecision : function(value)
36129     {
36130         if(this.thousandsDelimiter) {
36131             value += "";
36132             r = new RegExp(",", "g");
36133             value = value.replace(r, "");
36134         }
36135         
36136         var nan = isNaN(value);
36137         
36138         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36139             return nan ? '' : value;
36140         }
36141         return parseFloat(value).toFixed(this.decimalPrecision);
36142     },
36143
36144     setValue : function(v)
36145     {
36146         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36147         
36148         this.value = v;
36149         
36150         if(this.rendered){
36151             
36152             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36153             
36154             this.inputEl().dom.value = (v == '') ? '' :
36155                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36156             
36157             if(!this.allowZero && v === '0') {
36158                 this.hiddenEl().dom.value = '';
36159                 this.inputEl().dom.value = '';
36160             }
36161             
36162             this.validate();
36163         }
36164     },
36165
36166     decimalPrecisionFcn : function(v)
36167     {
36168         return Math.floor(v);
36169     },
36170
36171     beforeBlur : function()
36172     {
36173         var v = this.parseValue(this.getRawValue());
36174         
36175         if(v || v === 0 || v === ''){
36176             this.setValue(v);
36177         }
36178     },
36179     
36180     hiddenEl : function()
36181     {
36182         return this.el.select('input.hidden-number-input',true).first();
36183     }
36184     
36185 });
36186
36187  
36188
36189 /*
36190 * Licence: LGPL
36191 */
36192
36193 /**
36194  * @class Roo.bootstrap.DocumentSlider
36195  * @extends Roo.bootstrap.Component
36196  * Bootstrap DocumentSlider class
36197  * 
36198  * @constructor
36199  * Create a new DocumentViewer
36200  * @param {Object} config The config object
36201  */
36202
36203 Roo.bootstrap.DocumentSlider = function(config){
36204     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36205     
36206     this.files = [];
36207     
36208     this.addEvents({
36209         /**
36210          * @event initial
36211          * Fire after initEvent
36212          * @param {Roo.bootstrap.DocumentSlider} this
36213          */
36214         "initial" : true,
36215         /**
36216          * @event update
36217          * Fire after update
36218          * @param {Roo.bootstrap.DocumentSlider} this
36219          */
36220         "update" : true,
36221         /**
36222          * @event click
36223          * Fire after click
36224          * @param {Roo.bootstrap.DocumentSlider} this
36225          */
36226         "click" : true
36227     });
36228 };
36229
36230 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36231     
36232     files : false,
36233     
36234     indicator : 0,
36235     
36236     getAutoCreate : function()
36237     {
36238         var cfg = {
36239             tag : 'div',
36240             cls : 'roo-document-slider',
36241             cn : [
36242                 {
36243                     tag : 'div',
36244                     cls : 'roo-document-slider-header',
36245                     cn : [
36246                         {
36247                             tag : 'div',
36248                             cls : 'roo-document-slider-header-title'
36249                         }
36250                     ]
36251                 },
36252                 {
36253                     tag : 'div',
36254                     cls : 'roo-document-slider-body',
36255                     cn : [
36256                         {
36257                             tag : 'div',
36258                             cls : 'roo-document-slider-prev',
36259                             cn : [
36260                                 {
36261                                     tag : 'i',
36262                                     cls : 'fa fa-chevron-left'
36263                                 }
36264                             ]
36265                         },
36266                         {
36267                             tag : 'div',
36268                             cls : 'roo-document-slider-thumb',
36269                             cn : [
36270                                 {
36271                                     tag : 'img',
36272                                     cls : 'roo-document-slider-image'
36273                                 }
36274                             ]
36275                         },
36276                         {
36277                             tag : 'div',
36278                             cls : 'roo-document-slider-next',
36279                             cn : [
36280                                 {
36281                                     tag : 'i',
36282                                     cls : 'fa fa-chevron-right'
36283                                 }
36284                             ]
36285                         }
36286                     ]
36287                 }
36288             ]
36289         };
36290         
36291         return cfg;
36292     },
36293     
36294     initEvents : function()
36295     {
36296         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36297         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36298         
36299         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36300         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36301         
36302         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36303         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36304         
36305         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36306         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36307         
36308         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36309         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36310         
36311         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36312         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36313         
36314         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36315         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36316         
36317         this.thumbEl.on('click', this.onClick, this);
36318         
36319         this.prevIndicator.on('click', this.prev, this);
36320         
36321         this.nextIndicator.on('click', this.next, this);
36322         
36323     },
36324     
36325     initial : function()
36326     {
36327         if(this.files.length){
36328             this.indicator = 1;
36329             this.update()
36330         }
36331         
36332         this.fireEvent('initial', this);
36333     },
36334     
36335     update : function()
36336     {
36337         this.imageEl.attr('src', this.files[this.indicator - 1]);
36338         
36339         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36340         
36341         this.prevIndicator.show();
36342         
36343         if(this.indicator == 1){
36344             this.prevIndicator.hide();
36345         }
36346         
36347         this.nextIndicator.show();
36348         
36349         if(this.indicator == this.files.length){
36350             this.nextIndicator.hide();
36351         }
36352         
36353         this.thumbEl.scrollTo('top');
36354         
36355         this.fireEvent('update', this);
36356     },
36357     
36358     onClick : function(e)
36359     {
36360         e.preventDefault();
36361         
36362         this.fireEvent('click', this);
36363     },
36364     
36365     prev : function(e)
36366     {
36367         e.preventDefault();
36368         
36369         this.indicator = Math.max(1, this.indicator - 1);
36370         
36371         this.update();
36372     },
36373     
36374     next : function(e)
36375     {
36376         e.preventDefault();
36377         
36378         this.indicator = Math.min(this.files.length, this.indicator + 1);
36379         
36380         this.update();
36381     }
36382 });
36383 /*
36384  * - LGPL
36385  *
36386  * RadioSet
36387  *
36388  *
36389  */
36390
36391 /**
36392  * @class Roo.bootstrap.RadioSet
36393  * @extends Roo.bootstrap.Input
36394  * Bootstrap RadioSet class
36395  * @cfg {String} indicatorpos (left|right) default left
36396  * @cfg {Boolean} inline (true|false) inline the element (default true)
36397  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36398  * @constructor
36399  * Create a new RadioSet
36400  * @param {Object} config The config object
36401  */
36402
36403 Roo.bootstrap.RadioSet = function(config){
36404     
36405     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36406     
36407     this.radioes = [];
36408     
36409     Roo.bootstrap.RadioSet.register(this);
36410     
36411     this.addEvents({
36412         /**
36413         * @event check
36414         * Fires when the element is checked or unchecked.
36415         * @param {Roo.bootstrap.RadioSet} this This radio
36416         * @param {Roo.bootstrap.Radio} item The checked item
36417         */
36418        check : true,
36419        /**
36420         * @event click
36421         * Fires when the element is click.
36422         * @param {Roo.bootstrap.RadioSet} this This radio set
36423         * @param {Roo.bootstrap.Radio} item The checked item
36424         * @param {Roo.EventObject} e The event object
36425         */
36426        click : true
36427     });
36428     
36429 };
36430
36431 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36432
36433     radioes : false,
36434     
36435     inline : true,
36436     
36437     weight : '',
36438     
36439     indicatorpos : 'left',
36440     
36441     getAutoCreate : function()
36442     {
36443         var label = {
36444             tag : 'label',
36445             cls : 'roo-radio-set-label',
36446             cn : [
36447                 {
36448                     tag : 'span',
36449                     html : this.fieldLabel
36450                 }
36451             ]
36452         };
36453         if (Roo.bootstrap.version == 3) {
36454             
36455             
36456             if(this.indicatorpos == 'left'){
36457                 label.cn.unshift({
36458                     tag : 'i',
36459                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36460                     tooltip : 'This field is required'
36461                 });
36462             } else {
36463                 label.cn.push({
36464                     tag : 'i',
36465                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36466                     tooltip : 'This field is required'
36467                 });
36468             }
36469         }
36470         var items = {
36471             tag : 'div',
36472             cls : 'roo-radio-set-items'
36473         };
36474         
36475         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36476         
36477         if (align === 'left' && this.fieldLabel.length) {
36478             
36479             items = {
36480                 cls : "roo-radio-set-right", 
36481                 cn: [
36482                     items
36483                 ]
36484             };
36485             
36486             if(this.labelWidth > 12){
36487                 label.style = "width: " + this.labelWidth + 'px';
36488             }
36489             
36490             if(this.labelWidth < 13 && this.labelmd == 0){
36491                 this.labelmd = this.labelWidth;
36492             }
36493             
36494             if(this.labellg > 0){
36495                 label.cls += ' col-lg-' + this.labellg;
36496                 items.cls += ' col-lg-' + (12 - this.labellg);
36497             }
36498             
36499             if(this.labelmd > 0){
36500                 label.cls += ' col-md-' + this.labelmd;
36501                 items.cls += ' col-md-' + (12 - this.labelmd);
36502             }
36503             
36504             if(this.labelsm > 0){
36505                 label.cls += ' col-sm-' + this.labelsm;
36506                 items.cls += ' col-sm-' + (12 - this.labelsm);
36507             }
36508             
36509             if(this.labelxs > 0){
36510                 label.cls += ' col-xs-' + this.labelxs;
36511                 items.cls += ' col-xs-' + (12 - this.labelxs);
36512             }
36513         }
36514         
36515         var cfg = {
36516             tag : 'div',
36517             cls : 'roo-radio-set',
36518             cn : [
36519                 {
36520                     tag : 'input',
36521                     cls : 'roo-radio-set-input',
36522                     type : 'hidden',
36523                     name : this.name,
36524                     value : this.value ? this.value :  ''
36525                 },
36526                 label,
36527                 items
36528             ]
36529         };
36530         
36531         if(this.weight.length){
36532             cfg.cls += ' roo-radio-' + this.weight;
36533         }
36534         
36535         if(this.inline) {
36536             cfg.cls += ' roo-radio-set-inline';
36537         }
36538         
36539         var settings=this;
36540         ['xs','sm','md','lg'].map(function(size){
36541             if (settings[size]) {
36542                 cfg.cls += ' col-' + size + '-' + settings[size];
36543             }
36544         });
36545         
36546         return cfg;
36547         
36548     },
36549
36550     initEvents : function()
36551     {
36552         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36553         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36554         
36555         if(!this.fieldLabel.length){
36556             this.labelEl.hide();
36557         }
36558         
36559         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36560         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36561         
36562         this.indicator = this.indicatorEl();
36563         
36564         if(this.indicator){
36565             this.indicator.addClass('invisible');
36566         }
36567         
36568         this.originalValue = this.getValue();
36569         
36570     },
36571     
36572     inputEl: function ()
36573     {
36574         return this.el.select('.roo-radio-set-input', true).first();
36575     },
36576     
36577     getChildContainer : function()
36578     {
36579         return this.itemsEl;
36580     },
36581     
36582     register : function(item)
36583     {
36584         this.radioes.push(item);
36585         
36586     },
36587     
36588     validate : function()
36589     {   
36590         if(this.getVisibilityEl().hasClass('hidden')){
36591             return true;
36592         }
36593         
36594         var valid = false;
36595         
36596         Roo.each(this.radioes, function(i){
36597             if(!i.checked){
36598                 return;
36599             }
36600             
36601             valid = true;
36602             return false;
36603         });
36604         
36605         if(this.allowBlank) {
36606             return true;
36607         }
36608         
36609         if(this.disabled || valid){
36610             this.markValid();
36611             return true;
36612         }
36613         
36614         this.markInvalid();
36615         return false;
36616         
36617     },
36618     
36619     markValid : function()
36620     {
36621         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36622             this.indicatorEl().removeClass('visible');
36623             this.indicatorEl().addClass('invisible');
36624         }
36625         
36626         
36627         if (Roo.bootstrap.version == 3) {
36628             this.el.removeClass([this.invalidClass, this.validClass]);
36629             this.el.addClass(this.validClass);
36630         } else {
36631             this.el.removeClass(['is-invalid','is-valid']);
36632             this.el.addClass(['is-valid']);
36633         }
36634         this.fireEvent('valid', this);
36635     },
36636     
36637     markInvalid : function(msg)
36638     {
36639         if(this.allowBlank || this.disabled){
36640             return;
36641         }
36642         
36643         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36644             this.indicatorEl().removeClass('invisible');
36645             this.indicatorEl().addClass('visible');
36646         }
36647         if (Roo.bootstrap.version == 3) {
36648             this.el.removeClass([this.invalidClass, this.validClass]);
36649             this.el.addClass(this.invalidClass);
36650         } else {
36651             this.el.removeClass(['is-invalid','is-valid']);
36652             this.el.addClass(['is-invalid']);
36653         }
36654         
36655         this.fireEvent('invalid', this, msg);
36656         
36657     },
36658     
36659     setValue : function(v, suppressEvent)
36660     {   
36661         if(this.value === v){
36662             return;
36663         }
36664         
36665         this.value = v;
36666         
36667         if(this.rendered){
36668             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36669         }
36670         
36671         Roo.each(this.radioes, function(i){
36672             i.checked = false;
36673             i.el.removeClass('checked');
36674         });
36675         
36676         Roo.each(this.radioes, function(i){
36677             
36678             if(i.value === v || i.value.toString() === v.toString()){
36679                 i.checked = true;
36680                 i.el.addClass('checked');
36681                 
36682                 if(suppressEvent !== true){
36683                     this.fireEvent('check', this, i);
36684                 }
36685                 
36686                 return false;
36687             }
36688             
36689         }, this);
36690         
36691         this.validate();
36692     },
36693     
36694     clearInvalid : function(){
36695         
36696         if(!this.el || this.preventMark){
36697             return;
36698         }
36699         
36700         this.el.removeClass([this.invalidClass]);
36701         
36702         this.fireEvent('valid', this);
36703     }
36704     
36705 });
36706
36707 Roo.apply(Roo.bootstrap.RadioSet, {
36708     
36709     groups: {},
36710     
36711     register : function(set)
36712     {
36713         this.groups[set.name] = set;
36714     },
36715     
36716     get: function(name) 
36717     {
36718         if (typeof(this.groups[name]) == 'undefined') {
36719             return false;
36720         }
36721         
36722         return this.groups[name] ;
36723     }
36724     
36725 });
36726 /*
36727  * Based on:
36728  * Ext JS Library 1.1.1
36729  * Copyright(c) 2006-2007, Ext JS, LLC.
36730  *
36731  * Originally Released Under LGPL - original licence link has changed is not relivant.
36732  *
36733  * Fork - LGPL
36734  * <script type="text/javascript">
36735  */
36736
36737
36738 /**
36739  * @class Roo.bootstrap.SplitBar
36740  * @extends Roo.util.Observable
36741  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36742  * <br><br>
36743  * Usage:
36744  * <pre><code>
36745 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36746                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36747 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36748 split.minSize = 100;
36749 split.maxSize = 600;
36750 split.animate = true;
36751 split.on('moved', splitterMoved);
36752 </code></pre>
36753  * @constructor
36754  * Create a new SplitBar
36755  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36756  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36757  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36758  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36759                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36760                         position of the SplitBar).
36761  */
36762 Roo.bootstrap.SplitBar = function(cfg){
36763     
36764     /** @private */
36765     
36766     //{
36767     //  dragElement : elm
36768     //  resizingElement: el,
36769         // optional..
36770     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36771     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36772         // existingProxy ???
36773     //}
36774     
36775     this.el = Roo.get(cfg.dragElement, true);
36776     this.el.dom.unselectable = "on";
36777     /** @private */
36778     this.resizingEl = Roo.get(cfg.resizingElement, true);
36779
36780     /**
36781      * @private
36782      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36783      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36784      * @type Number
36785      */
36786     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36787     
36788     /**
36789      * The minimum size of the resizing element. (Defaults to 0)
36790      * @type Number
36791      */
36792     this.minSize = 0;
36793     
36794     /**
36795      * The maximum size of the resizing element. (Defaults to 2000)
36796      * @type Number
36797      */
36798     this.maxSize = 2000;
36799     
36800     /**
36801      * Whether to animate the transition to the new size
36802      * @type Boolean
36803      */
36804     this.animate = false;
36805     
36806     /**
36807      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36808      * @type Boolean
36809      */
36810     this.useShim = false;
36811     
36812     /** @private */
36813     this.shim = null;
36814     
36815     if(!cfg.existingProxy){
36816         /** @private */
36817         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36818     }else{
36819         this.proxy = Roo.get(cfg.existingProxy).dom;
36820     }
36821     /** @private */
36822     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36823     
36824     /** @private */
36825     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36826     
36827     /** @private */
36828     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36829     
36830     /** @private */
36831     this.dragSpecs = {};
36832     
36833     /**
36834      * @private The adapter to use to positon and resize elements
36835      */
36836     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36837     this.adapter.init(this);
36838     
36839     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36840         /** @private */
36841         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36842         this.el.addClass("roo-splitbar-h");
36843     }else{
36844         /** @private */
36845         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36846         this.el.addClass("roo-splitbar-v");
36847     }
36848     
36849     this.addEvents({
36850         /**
36851          * @event resize
36852          * Fires when the splitter is moved (alias for {@link #event-moved})
36853          * @param {Roo.bootstrap.SplitBar} this
36854          * @param {Number} newSize the new width or height
36855          */
36856         "resize" : true,
36857         /**
36858          * @event moved
36859          * Fires when the splitter is moved
36860          * @param {Roo.bootstrap.SplitBar} this
36861          * @param {Number} newSize the new width or height
36862          */
36863         "moved" : true,
36864         /**
36865          * @event beforeresize
36866          * Fires before the splitter is dragged
36867          * @param {Roo.bootstrap.SplitBar} this
36868          */
36869         "beforeresize" : true,
36870
36871         "beforeapply" : true
36872     });
36873
36874     Roo.util.Observable.call(this);
36875 };
36876
36877 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36878     onStartProxyDrag : function(x, y){
36879         this.fireEvent("beforeresize", this);
36880         if(!this.overlay){
36881             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36882             o.unselectable();
36883             o.enableDisplayMode("block");
36884             // all splitbars share the same overlay
36885             Roo.bootstrap.SplitBar.prototype.overlay = o;
36886         }
36887         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36888         this.overlay.show();
36889         Roo.get(this.proxy).setDisplayed("block");
36890         var size = this.adapter.getElementSize(this);
36891         this.activeMinSize = this.getMinimumSize();;
36892         this.activeMaxSize = this.getMaximumSize();;
36893         var c1 = size - this.activeMinSize;
36894         var c2 = Math.max(this.activeMaxSize - size, 0);
36895         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36896             this.dd.resetConstraints();
36897             this.dd.setXConstraint(
36898                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36899                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36900             );
36901             this.dd.setYConstraint(0, 0);
36902         }else{
36903             this.dd.resetConstraints();
36904             this.dd.setXConstraint(0, 0);
36905             this.dd.setYConstraint(
36906                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36907                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36908             );
36909          }
36910         this.dragSpecs.startSize = size;
36911         this.dragSpecs.startPoint = [x, y];
36912         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36913     },
36914     
36915     /** 
36916      * @private Called after the drag operation by the DDProxy
36917      */
36918     onEndProxyDrag : function(e){
36919         Roo.get(this.proxy).setDisplayed(false);
36920         var endPoint = Roo.lib.Event.getXY(e);
36921         if(this.overlay){
36922             this.overlay.hide();
36923         }
36924         var newSize;
36925         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36926             newSize = this.dragSpecs.startSize + 
36927                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36928                     endPoint[0] - this.dragSpecs.startPoint[0] :
36929                     this.dragSpecs.startPoint[0] - endPoint[0]
36930                 );
36931         }else{
36932             newSize = this.dragSpecs.startSize + 
36933                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36934                     endPoint[1] - this.dragSpecs.startPoint[1] :
36935                     this.dragSpecs.startPoint[1] - endPoint[1]
36936                 );
36937         }
36938         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36939         if(newSize != this.dragSpecs.startSize){
36940             if(this.fireEvent('beforeapply', this, newSize) !== false){
36941                 this.adapter.setElementSize(this, newSize);
36942                 this.fireEvent("moved", this, newSize);
36943                 this.fireEvent("resize", this, newSize);
36944             }
36945         }
36946     },
36947     
36948     /**
36949      * Get the adapter this SplitBar uses
36950      * @return The adapter object
36951      */
36952     getAdapter : function(){
36953         return this.adapter;
36954     },
36955     
36956     /**
36957      * Set the adapter this SplitBar uses
36958      * @param {Object} adapter A SplitBar adapter object
36959      */
36960     setAdapter : function(adapter){
36961         this.adapter = adapter;
36962         this.adapter.init(this);
36963     },
36964     
36965     /**
36966      * Gets the minimum size for the resizing element
36967      * @return {Number} The minimum size
36968      */
36969     getMinimumSize : function(){
36970         return this.minSize;
36971     },
36972     
36973     /**
36974      * Sets the minimum size for the resizing element
36975      * @param {Number} minSize The minimum size
36976      */
36977     setMinimumSize : function(minSize){
36978         this.minSize = minSize;
36979     },
36980     
36981     /**
36982      * Gets the maximum size for the resizing element
36983      * @return {Number} The maximum size
36984      */
36985     getMaximumSize : function(){
36986         return this.maxSize;
36987     },
36988     
36989     /**
36990      * Sets the maximum size for the resizing element
36991      * @param {Number} maxSize The maximum size
36992      */
36993     setMaximumSize : function(maxSize){
36994         this.maxSize = maxSize;
36995     },
36996     
36997     /**
36998      * Sets the initialize size for the resizing element
36999      * @param {Number} size The initial size
37000      */
37001     setCurrentSize : function(size){
37002         var oldAnimate = this.animate;
37003         this.animate = false;
37004         this.adapter.setElementSize(this, size);
37005         this.animate = oldAnimate;
37006     },
37007     
37008     /**
37009      * Destroy this splitbar. 
37010      * @param {Boolean} removeEl True to remove the element
37011      */
37012     destroy : function(removeEl){
37013         if(this.shim){
37014             this.shim.remove();
37015         }
37016         this.dd.unreg();
37017         this.proxy.parentNode.removeChild(this.proxy);
37018         if(removeEl){
37019             this.el.remove();
37020         }
37021     }
37022 });
37023
37024 /**
37025  * @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.
37026  */
37027 Roo.bootstrap.SplitBar.createProxy = function(dir){
37028     var proxy = new Roo.Element(document.createElement("div"));
37029     proxy.unselectable();
37030     var cls = 'roo-splitbar-proxy';
37031     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37032     document.body.appendChild(proxy.dom);
37033     return proxy.dom;
37034 };
37035
37036 /** 
37037  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37038  * Default Adapter. It assumes the splitter and resizing element are not positioned
37039  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37040  */
37041 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37042 };
37043
37044 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37045     // do nothing for now
37046     init : function(s){
37047     
37048     },
37049     /**
37050      * Called before drag operations to get the current size of the resizing element. 
37051      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37052      */
37053      getElementSize : function(s){
37054         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37055             return s.resizingEl.getWidth();
37056         }else{
37057             return s.resizingEl.getHeight();
37058         }
37059     },
37060     
37061     /**
37062      * Called after drag operations to set the size of the resizing element.
37063      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37064      * @param {Number} newSize The new size to set
37065      * @param {Function} onComplete A function to be invoked when resizing is complete
37066      */
37067     setElementSize : function(s, newSize, onComplete){
37068         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37069             if(!s.animate){
37070                 s.resizingEl.setWidth(newSize);
37071                 if(onComplete){
37072                     onComplete(s, newSize);
37073                 }
37074             }else{
37075                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37076             }
37077         }else{
37078             
37079             if(!s.animate){
37080                 s.resizingEl.setHeight(newSize);
37081                 if(onComplete){
37082                     onComplete(s, newSize);
37083                 }
37084             }else{
37085                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37086             }
37087         }
37088     }
37089 };
37090
37091 /** 
37092  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37093  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37094  * Adapter that  moves the splitter element to align with the resized sizing element. 
37095  * Used with an absolute positioned SplitBar.
37096  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37097  * document.body, make sure you assign an id to the body element.
37098  */
37099 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37100     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37101     this.container = Roo.get(container);
37102 };
37103
37104 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37105     init : function(s){
37106         this.basic.init(s);
37107     },
37108     
37109     getElementSize : function(s){
37110         return this.basic.getElementSize(s);
37111     },
37112     
37113     setElementSize : function(s, newSize, onComplete){
37114         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37115     },
37116     
37117     moveSplitter : function(s){
37118         var yes = Roo.bootstrap.SplitBar;
37119         switch(s.placement){
37120             case yes.LEFT:
37121                 s.el.setX(s.resizingEl.getRight());
37122                 break;
37123             case yes.RIGHT:
37124                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37125                 break;
37126             case yes.TOP:
37127                 s.el.setY(s.resizingEl.getBottom());
37128                 break;
37129             case yes.BOTTOM:
37130                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37131                 break;
37132         }
37133     }
37134 };
37135
37136 /**
37137  * Orientation constant - Create a vertical SplitBar
37138  * @static
37139  * @type Number
37140  */
37141 Roo.bootstrap.SplitBar.VERTICAL = 1;
37142
37143 /**
37144  * Orientation constant - Create a horizontal SplitBar
37145  * @static
37146  * @type Number
37147  */
37148 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37149
37150 /**
37151  * Placement constant - The resizing element is to the left of the splitter element
37152  * @static
37153  * @type Number
37154  */
37155 Roo.bootstrap.SplitBar.LEFT = 1;
37156
37157 /**
37158  * Placement constant - The resizing element is to the right of the splitter element
37159  * @static
37160  * @type Number
37161  */
37162 Roo.bootstrap.SplitBar.RIGHT = 2;
37163
37164 /**
37165  * Placement constant - The resizing element is positioned above the splitter element
37166  * @static
37167  * @type Number
37168  */
37169 Roo.bootstrap.SplitBar.TOP = 3;
37170
37171 /**
37172  * Placement constant - The resizing element is positioned under splitter element
37173  * @static
37174  * @type Number
37175  */
37176 Roo.bootstrap.SplitBar.BOTTOM = 4;
37177 Roo.namespace("Roo.bootstrap.layout");/*
37178  * Based on:
37179  * Ext JS Library 1.1.1
37180  * Copyright(c) 2006-2007, Ext JS, LLC.
37181  *
37182  * Originally Released Under LGPL - original licence link has changed is not relivant.
37183  *
37184  * Fork - LGPL
37185  * <script type="text/javascript">
37186  */
37187
37188 /**
37189  * @class Roo.bootstrap.layout.Manager
37190  * @extends Roo.bootstrap.Component
37191  * Base class for layout managers.
37192  */
37193 Roo.bootstrap.layout.Manager = function(config)
37194 {
37195     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37196
37197
37198
37199
37200
37201     /** false to disable window resize monitoring @type Boolean */
37202     this.monitorWindowResize = true;
37203     this.regions = {};
37204     this.addEvents({
37205         /**
37206          * @event layout
37207          * Fires when a layout is performed.
37208          * @param {Roo.LayoutManager} this
37209          */
37210         "layout" : true,
37211         /**
37212          * @event regionresized
37213          * Fires when the user resizes a region.
37214          * @param {Roo.LayoutRegion} region The resized region
37215          * @param {Number} newSize The new size (width for east/west, height for north/south)
37216          */
37217         "regionresized" : true,
37218         /**
37219          * @event regioncollapsed
37220          * Fires when a region is collapsed.
37221          * @param {Roo.LayoutRegion} region The collapsed region
37222          */
37223         "regioncollapsed" : true,
37224         /**
37225          * @event regionexpanded
37226          * Fires when a region is expanded.
37227          * @param {Roo.LayoutRegion} region The expanded region
37228          */
37229         "regionexpanded" : true
37230     });
37231     this.updating = false;
37232
37233     if (config.el) {
37234         this.el = Roo.get(config.el);
37235         this.initEvents();
37236     }
37237
37238 };
37239
37240 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37241
37242
37243     regions : null,
37244
37245     monitorWindowResize : true,
37246
37247
37248     updating : false,
37249
37250
37251     onRender : function(ct, position)
37252     {
37253         if(!this.el){
37254             this.el = Roo.get(ct);
37255             this.initEvents();
37256         }
37257         //this.fireEvent('render',this);
37258     },
37259
37260
37261     initEvents: function()
37262     {
37263
37264
37265         // ie scrollbar fix
37266         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37267             document.body.scroll = "no";
37268         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37269             this.el.position('relative');
37270         }
37271         this.id = this.el.id;
37272         this.el.addClass("roo-layout-container");
37273         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37274         if(this.el.dom != document.body ) {
37275             this.el.on('resize', this.layout,this);
37276             this.el.on('show', this.layout,this);
37277         }
37278
37279     },
37280
37281     /**
37282      * Returns true if this layout is currently being updated
37283      * @return {Boolean}
37284      */
37285     isUpdating : function(){
37286         return this.updating;
37287     },
37288
37289     /**
37290      * Suspend the LayoutManager from doing auto-layouts while
37291      * making multiple add or remove calls
37292      */
37293     beginUpdate : function(){
37294         this.updating = true;
37295     },
37296
37297     /**
37298      * Restore auto-layouts and optionally disable the manager from performing a layout
37299      * @param {Boolean} noLayout true to disable a layout update
37300      */
37301     endUpdate : function(noLayout){
37302         this.updating = false;
37303         if(!noLayout){
37304             this.layout();
37305         }
37306     },
37307
37308     layout: function(){
37309         // abstract...
37310     },
37311
37312     onRegionResized : function(region, newSize){
37313         this.fireEvent("regionresized", region, newSize);
37314         this.layout();
37315     },
37316
37317     onRegionCollapsed : function(region){
37318         this.fireEvent("regioncollapsed", region);
37319     },
37320
37321     onRegionExpanded : function(region){
37322         this.fireEvent("regionexpanded", region);
37323     },
37324
37325     /**
37326      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37327      * performs box-model adjustments.
37328      * @return {Object} The size as an object {width: (the width), height: (the height)}
37329      */
37330     getViewSize : function()
37331     {
37332         var size;
37333         if(this.el.dom != document.body){
37334             size = this.el.getSize();
37335         }else{
37336             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37337         }
37338         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37339         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37340         return size;
37341     },
37342
37343     /**
37344      * Returns the Element this layout is bound to.
37345      * @return {Roo.Element}
37346      */
37347     getEl : function(){
37348         return this.el;
37349     },
37350
37351     /**
37352      * Returns the specified region.
37353      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37354      * @return {Roo.LayoutRegion}
37355      */
37356     getRegion : function(target){
37357         return this.regions[target.toLowerCase()];
37358     },
37359
37360     onWindowResize : function(){
37361         if(this.monitorWindowResize){
37362             this.layout();
37363         }
37364     }
37365 });
37366 /*
37367  * Based on:
37368  * Ext JS Library 1.1.1
37369  * Copyright(c) 2006-2007, Ext JS, LLC.
37370  *
37371  * Originally Released Under LGPL - original licence link has changed is not relivant.
37372  *
37373  * Fork - LGPL
37374  * <script type="text/javascript">
37375  */
37376 /**
37377  * @class Roo.bootstrap.layout.Border
37378  * @extends Roo.bootstrap.layout.Manager
37379  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37380  * please see: examples/bootstrap/nested.html<br><br>
37381  
37382 <b>The container the layout is rendered into can be either the body element or any other element.
37383 If it is not the body element, the container needs to either be an absolute positioned element,
37384 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37385 the container size if it is not the body element.</b>
37386
37387 * @constructor
37388 * Create a new Border
37389 * @param {Object} config Configuration options
37390  */
37391 Roo.bootstrap.layout.Border = function(config){
37392     config = config || {};
37393     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37394     
37395     
37396     
37397     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37398         if(config[region]){
37399             config[region].region = region;
37400             this.addRegion(config[region]);
37401         }
37402     },this);
37403     
37404 };
37405
37406 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37407
37408 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37409     
37410     parent : false, // this might point to a 'nest' or a ???
37411     
37412     /**
37413      * Creates and adds a new region if it doesn't already exist.
37414      * @param {String} target The target region key (north, south, east, west or center).
37415      * @param {Object} config The regions config object
37416      * @return {BorderLayoutRegion} The new region
37417      */
37418     addRegion : function(config)
37419     {
37420         if(!this.regions[config.region]){
37421             var r = this.factory(config);
37422             this.bindRegion(r);
37423         }
37424         return this.regions[config.region];
37425     },
37426
37427     // private (kinda)
37428     bindRegion : function(r){
37429         this.regions[r.config.region] = r;
37430         
37431         r.on("visibilitychange",    this.layout, this);
37432         r.on("paneladded",          this.layout, this);
37433         r.on("panelremoved",        this.layout, this);
37434         r.on("invalidated",         this.layout, this);
37435         r.on("resized",             this.onRegionResized, this);
37436         r.on("collapsed",           this.onRegionCollapsed, this);
37437         r.on("expanded",            this.onRegionExpanded, this);
37438     },
37439
37440     /**
37441      * Performs a layout update.
37442      */
37443     layout : function()
37444     {
37445         if(this.updating) {
37446             return;
37447         }
37448         
37449         // render all the rebions if they have not been done alreayd?
37450         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37451             if(this.regions[region] && !this.regions[region].bodyEl){
37452                 this.regions[region].onRender(this.el)
37453             }
37454         },this);
37455         
37456         var size = this.getViewSize();
37457         var w = size.width;
37458         var h = size.height;
37459         var centerW = w;
37460         var centerH = h;
37461         var centerY = 0;
37462         var centerX = 0;
37463         //var x = 0, y = 0;
37464
37465         var rs = this.regions;
37466         var north = rs["north"];
37467         var south = rs["south"]; 
37468         var west = rs["west"];
37469         var east = rs["east"];
37470         var center = rs["center"];
37471         //if(this.hideOnLayout){ // not supported anymore
37472             //c.el.setStyle("display", "none");
37473         //}
37474         if(north && north.isVisible()){
37475             var b = north.getBox();
37476             var m = north.getMargins();
37477             b.width = w - (m.left+m.right);
37478             b.x = m.left;
37479             b.y = m.top;
37480             centerY = b.height + b.y + m.bottom;
37481             centerH -= centerY;
37482             north.updateBox(this.safeBox(b));
37483         }
37484         if(south && south.isVisible()){
37485             var b = south.getBox();
37486             var m = south.getMargins();
37487             b.width = w - (m.left+m.right);
37488             b.x = m.left;
37489             var totalHeight = (b.height + m.top + m.bottom);
37490             b.y = h - totalHeight + m.top;
37491             centerH -= totalHeight;
37492             south.updateBox(this.safeBox(b));
37493         }
37494         if(west && west.isVisible()){
37495             var b = west.getBox();
37496             var m = west.getMargins();
37497             b.height = centerH - (m.top+m.bottom);
37498             b.x = m.left;
37499             b.y = centerY + m.top;
37500             var totalWidth = (b.width + m.left + m.right);
37501             centerX += totalWidth;
37502             centerW -= totalWidth;
37503             west.updateBox(this.safeBox(b));
37504         }
37505         if(east && east.isVisible()){
37506             var b = east.getBox();
37507             var m = east.getMargins();
37508             b.height = centerH - (m.top+m.bottom);
37509             var totalWidth = (b.width + m.left + m.right);
37510             b.x = w - totalWidth + m.left;
37511             b.y = centerY + m.top;
37512             centerW -= totalWidth;
37513             east.updateBox(this.safeBox(b));
37514         }
37515         if(center){
37516             var m = center.getMargins();
37517             var centerBox = {
37518                 x: centerX + m.left,
37519                 y: centerY + m.top,
37520                 width: centerW - (m.left+m.right),
37521                 height: centerH - (m.top+m.bottom)
37522             };
37523             //if(this.hideOnLayout){
37524                 //center.el.setStyle("display", "block");
37525             //}
37526             center.updateBox(this.safeBox(centerBox));
37527         }
37528         this.el.repaint();
37529         this.fireEvent("layout", this);
37530     },
37531
37532     // private
37533     safeBox : function(box){
37534         box.width = Math.max(0, box.width);
37535         box.height = Math.max(0, box.height);
37536         return box;
37537     },
37538
37539     /**
37540      * Adds a ContentPanel (or subclass) to this layout.
37541      * @param {String} target The target region key (north, south, east, west or center).
37542      * @param {Roo.ContentPanel} panel The panel to add
37543      * @return {Roo.ContentPanel} The added panel
37544      */
37545     add : function(target, panel){
37546          
37547         target = target.toLowerCase();
37548         return this.regions[target].add(panel);
37549     },
37550
37551     /**
37552      * Remove a ContentPanel (or subclass) to this layout.
37553      * @param {String} target The target region key (north, south, east, west or center).
37554      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37555      * @return {Roo.ContentPanel} The removed panel
37556      */
37557     remove : function(target, panel){
37558         target = target.toLowerCase();
37559         return this.regions[target].remove(panel);
37560     },
37561
37562     /**
37563      * Searches all regions for a panel with the specified id
37564      * @param {String} panelId
37565      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37566      */
37567     findPanel : function(panelId){
37568         var rs = this.regions;
37569         for(var target in rs){
37570             if(typeof rs[target] != "function"){
37571                 var p = rs[target].getPanel(panelId);
37572                 if(p){
37573                     return p;
37574                 }
37575             }
37576         }
37577         return null;
37578     },
37579
37580     /**
37581      * Searches all regions for a panel with the specified id and activates (shows) it.
37582      * @param {String/ContentPanel} panelId The panels id or the panel itself
37583      * @return {Roo.ContentPanel} The shown panel or null
37584      */
37585     showPanel : function(panelId) {
37586       var rs = this.regions;
37587       for(var target in rs){
37588          var r = rs[target];
37589          if(typeof r != "function"){
37590             if(r.hasPanel(panelId)){
37591                return r.showPanel(panelId);
37592             }
37593          }
37594       }
37595       return null;
37596    },
37597
37598    /**
37599      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37600      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37601      */
37602    /*
37603     restoreState : function(provider){
37604         if(!provider){
37605             provider = Roo.state.Manager;
37606         }
37607         var sm = new Roo.LayoutStateManager();
37608         sm.init(this, provider);
37609     },
37610 */
37611  
37612  
37613     /**
37614      * Adds a xtype elements to the layout.
37615      * <pre><code>
37616
37617 layout.addxtype({
37618        xtype : 'ContentPanel',
37619        region: 'west',
37620        items: [ .... ]
37621    }
37622 );
37623
37624 layout.addxtype({
37625         xtype : 'NestedLayoutPanel',
37626         region: 'west',
37627         layout: {
37628            center: { },
37629            west: { }   
37630         },
37631         items : [ ... list of content panels or nested layout panels.. ]
37632    }
37633 );
37634 </code></pre>
37635      * @param {Object} cfg Xtype definition of item to add.
37636      */
37637     addxtype : function(cfg)
37638     {
37639         // basically accepts a pannel...
37640         // can accept a layout region..!?!?
37641         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37642         
37643         
37644         // theory?  children can only be panels??
37645         
37646         //if (!cfg.xtype.match(/Panel$/)) {
37647         //    return false;
37648         //}
37649         var ret = false;
37650         
37651         if (typeof(cfg.region) == 'undefined') {
37652             Roo.log("Failed to add Panel, region was not set");
37653             Roo.log(cfg);
37654             return false;
37655         }
37656         var region = cfg.region;
37657         delete cfg.region;
37658         
37659           
37660         var xitems = [];
37661         if (cfg.items) {
37662             xitems = cfg.items;
37663             delete cfg.items;
37664         }
37665         var nb = false;
37666         
37667         if ( region == 'center') {
37668             Roo.log("Center: " + cfg.title);
37669         }
37670         
37671         
37672         switch(cfg.xtype) 
37673         {
37674             case 'Content':  // ContentPanel (el, cfg)
37675             case 'Scroll':  // ContentPanel (el, cfg)
37676             case 'View': 
37677                 cfg.autoCreate = cfg.autoCreate || true;
37678                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37679                 //} else {
37680                 //    var el = this.el.createChild();
37681                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37682                 //}
37683                 
37684                 this.add(region, ret);
37685                 break;
37686             
37687             /*
37688             case 'TreePanel': // our new panel!
37689                 cfg.el = this.el.createChild();
37690                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37691                 this.add(region, ret);
37692                 break;
37693             */
37694             
37695             case 'Nest': 
37696                 // create a new Layout (which is  a Border Layout...
37697                 
37698                 var clayout = cfg.layout;
37699                 clayout.el  = this.el.createChild();
37700                 clayout.items   = clayout.items  || [];
37701                 
37702                 delete cfg.layout;
37703                 
37704                 // replace this exitems with the clayout ones..
37705                 xitems = clayout.items;
37706                  
37707                 // force background off if it's in center...
37708                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37709                     cfg.background = false;
37710                 }
37711                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37712                 
37713                 
37714                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37715                 //console.log('adding nested layout panel '  + cfg.toSource());
37716                 this.add(region, ret);
37717                 nb = {}; /// find first...
37718                 break;
37719             
37720             case 'Grid':
37721                 
37722                 // needs grid and region
37723                 
37724                 //var el = this.getRegion(region).el.createChild();
37725                 /*
37726                  *var el = this.el.createChild();
37727                 // create the grid first...
37728                 cfg.grid.container = el;
37729                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37730                 */
37731                 
37732                 if (region == 'center' && this.active ) {
37733                     cfg.background = false;
37734                 }
37735                 
37736                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37737                 
37738                 this.add(region, ret);
37739                 /*
37740                 if (cfg.background) {
37741                     // render grid on panel activation (if panel background)
37742                     ret.on('activate', function(gp) {
37743                         if (!gp.grid.rendered) {
37744                     //        gp.grid.render(el);
37745                         }
37746                     });
37747                 } else {
37748                   //  cfg.grid.render(el);
37749                 }
37750                 */
37751                 break;
37752            
37753            
37754             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37755                 // it was the old xcomponent building that caused this before.
37756                 // espeically if border is the top element in the tree.
37757                 ret = this;
37758                 break; 
37759                 
37760                     
37761                 
37762                 
37763                 
37764             default:
37765                 /*
37766                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37767                     
37768                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37769                     this.add(region, ret);
37770                 } else {
37771                 */
37772                     Roo.log(cfg);
37773                     throw "Can not add '" + cfg.xtype + "' to Border";
37774                     return null;
37775              
37776                                 
37777              
37778         }
37779         this.beginUpdate();
37780         // add children..
37781         var region = '';
37782         var abn = {};
37783         Roo.each(xitems, function(i)  {
37784             region = nb && i.region ? i.region : false;
37785             
37786             var add = ret.addxtype(i);
37787            
37788             if (region) {
37789                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37790                 if (!i.background) {
37791                     abn[region] = nb[region] ;
37792                 }
37793             }
37794             
37795         });
37796         this.endUpdate();
37797
37798         // make the last non-background panel active..
37799         //if (nb) { Roo.log(abn); }
37800         if (nb) {
37801             
37802             for(var r in abn) {
37803                 region = this.getRegion(r);
37804                 if (region) {
37805                     // tried using nb[r], but it does not work..
37806                      
37807                     region.showPanel(abn[r]);
37808                    
37809                 }
37810             }
37811         }
37812         return ret;
37813         
37814     },
37815     
37816     
37817 // private
37818     factory : function(cfg)
37819     {
37820         
37821         var validRegions = Roo.bootstrap.layout.Border.regions;
37822
37823         var target = cfg.region;
37824         cfg.mgr = this;
37825         
37826         var r = Roo.bootstrap.layout;
37827         Roo.log(target);
37828         switch(target){
37829             case "north":
37830                 return new r.North(cfg);
37831             case "south":
37832                 return new r.South(cfg);
37833             case "east":
37834                 return new r.East(cfg);
37835             case "west":
37836                 return new r.West(cfg);
37837             case "center":
37838                 return new r.Center(cfg);
37839         }
37840         throw 'Layout region "'+target+'" not supported.';
37841     }
37842     
37843     
37844 });
37845  /*
37846  * Based on:
37847  * Ext JS Library 1.1.1
37848  * Copyright(c) 2006-2007, Ext JS, LLC.
37849  *
37850  * Originally Released Under LGPL - original licence link has changed is not relivant.
37851  *
37852  * Fork - LGPL
37853  * <script type="text/javascript">
37854  */
37855  
37856 /**
37857  * @class Roo.bootstrap.layout.Basic
37858  * @extends Roo.util.Observable
37859  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37860  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37861  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37862  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37863  * @cfg {string}   region  the region that it inhabits..
37864  * @cfg {bool}   skipConfig skip config?
37865  * 
37866
37867  */
37868 Roo.bootstrap.layout.Basic = function(config){
37869     
37870     this.mgr = config.mgr;
37871     
37872     this.position = config.region;
37873     
37874     var skipConfig = config.skipConfig;
37875     
37876     this.events = {
37877         /**
37878          * @scope Roo.BasicLayoutRegion
37879          */
37880         
37881         /**
37882          * @event beforeremove
37883          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37884          * @param {Roo.LayoutRegion} this
37885          * @param {Roo.ContentPanel} panel The panel
37886          * @param {Object} e The cancel event object
37887          */
37888         "beforeremove" : true,
37889         /**
37890          * @event invalidated
37891          * Fires when the layout for this region is changed.
37892          * @param {Roo.LayoutRegion} this
37893          */
37894         "invalidated" : true,
37895         /**
37896          * @event visibilitychange
37897          * Fires when this region is shown or hidden 
37898          * @param {Roo.LayoutRegion} this
37899          * @param {Boolean} visibility true or false
37900          */
37901         "visibilitychange" : true,
37902         /**
37903          * @event paneladded
37904          * Fires when a panel is added. 
37905          * @param {Roo.LayoutRegion} this
37906          * @param {Roo.ContentPanel} panel The panel
37907          */
37908         "paneladded" : true,
37909         /**
37910          * @event panelremoved
37911          * Fires when a panel is removed. 
37912          * @param {Roo.LayoutRegion} this
37913          * @param {Roo.ContentPanel} panel The panel
37914          */
37915         "panelremoved" : true,
37916         /**
37917          * @event beforecollapse
37918          * Fires when this region before collapse.
37919          * @param {Roo.LayoutRegion} this
37920          */
37921         "beforecollapse" : true,
37922         /**
37923          * @event collapsed
37924          * Fires when this region is collapsed.
37925          * @param {Roo.LayoutRegion} this
37926          */
37927         "collapsed" : true,
37928         /**
37929          * @event expanded
37930          * Fires when this region is expanded.
37931          * @param {Roo.LayoutRegion} this
37932          */
37933         "expanded" : true,
37934         /**
37935          * @event slideshow
37936          * Fires when this region is slid into view.
37937          * @param {Roo.LayoutRegion} this
37938          */
37939         "slideshow" : true,
37940         /**
37941          * @event slidehide
37942          * Fires when this region slides out of view. 
37943          * @param {Roo.LayoutRegion} this
37944          */
37945         "slidehide" : true,
37946         /**
37947          * @event panelactivated
37948          * Fires when a panel is activated. 
37949          * @param {Roo.LayoutRegion} this
37950          * @param {Roo.ContentPanel} panel The activated panel
37951          */
37952         "panelactivated" : true,
37953         /**
37954          * @event resized
37955          * Fires when the user resizes this region. 
37956          * @param {Roo.LayoutRegion} this
37957          * @param {Number} newSize The new size (width for east/west, height for north/south)
37958          */
37959         "resized" : true
37960     };
37961     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37962     this.panels = new Roo.util.MixedCollection();
37963     this.panels.getKey = this.getPanelId.createDelegate(this);
37964     this.box = null;
37965     this.activePanel = null;
37966     // ensure listeners are added...
37967     
37968     if (config.listeners || config.events) {
37969         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37970             listeners : config.listeners || {},
37971             events : config.events || {}
37972         });
37973     }
37974     
37975     if(skipConfig !== true){
37976         this.applyConfig(config);
37977     }
37978 };
37979
37980 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37981 {
37982     getPanelId : function(p){
37983         return p.getId();
37984     },
37985     
37986     applyConfig : function(config){
37987         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37988         this.config = config;
37989         
37990     },
37991     
37992     /**
37993      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37994      * the width, for horizontal (north, south) the height.
37995      * @param {Number} newSize The new width or height
37996      */
37997     resizeTo : function(newSize){
37998         var el = this.el ? this.el :
37999                  (this.activePanel ? this.activePanel.getEl() : null);
38000         if(el){
38001             switch(this.position){
38002                 case "east":
38003                 case "west":
38004                     el.setWidth(newSize);
38005                     this.fireEvent("resized", this, newSize);
38006                 break;
38007                 case "north":
38008                 case "south":
38009                     el.setHeight(newSize);
38010                     this.fireEvent("resized", this, newSize);
38011                 break;                
38012             }
38013         }
38014     },
38015     
38016     getBox : function(){
38017         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38018     },
38019     
38020     getMargins : function(){
38021         return this.margins;
38022     },
38023     
38024     updateBox : function(box){
38025         this.box = box;
38026         var el = this.activePanel.getEl();
38027         el.dom.style.left = box.x + "px";
38028         el.dom.style.top = box.y + "px";
38029         this.activePanel.setSize(box.width, box.height);
38030     },
38031     
38032     /**
38033      * Returns the container element for this region.
38034      * @return {Roo.Element}
38035      */
38036     getEl : function(){
38037         return this.activePanel;
38038     },
38039     
38040     /**
38041      * Returns true if this region is currently visible.
38042      * @return {Boolean}
38043      */
38044     isVisible : function(){
38045         return this.activePanel ? true : false;
38046     },
38047     
38048     setActivePanel : function(panel){
38049         panel = this.getPanel(panel);
38050         if(this.activePanel && this.activePanel != panel){
38051             this.activePanel.setActiveState(false);
38052             this.activePanel.getEl().setLeftTop(-10000,-10000);
38053         }
38054         this.activePanel = panel;
38055         panel.setActiveState(true);
38056         if(this.box){
38057             panel.setSize(this.box.width, this.box.height);
38058         }
38059         this.fireEvent("panelactivated", this, panel);
38060         this.fireEvent("invalidated");
38061     },
38062     
38063     /**
38064      * Show the specified panel.
38065      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38066      * @return {Roo.ContentPanel} The shown panel or null
38067      */
38068     showPanel : function(panel){
38069         panel = this.getPanel(panel);
38070         if(panel){
38071             this.setActivePanel(panel);
38072         }
38073         return panel;
38074     },
38075     
38076     /**
38077      * Get the active panel for this region.
38078      * @return {Roo.ContentPanel} The active panel or null
38079      */
38080     getActivePanel : function(){
38081         return this.activePanel;
38082     },
38083     
38084     /**
38085      * Add the passed ContentPanel(s)
38086      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38087      * @return {Roo.ContentPanel} The panel added (if only one was added)
38088      */
38089     add : function(panel){
38090         if(arguments.length > 1){
38091             for(var i = 0, len = arguments.length; i < len; i++) {
38092                 this.add(arguments[i]);
38093             }
38094             return null;
38095         }
38096         if(this.hasPanel(panel)){
38097             this.showPanel(panel);
38098             return panel;
38099         }
38100         var el = panel.getEl();
38101         if(el.dom.parentNode != this.mgr.el.dom){
38102             this.mgr.el.dom.appendChild(el.dom);
38103         }
38104         if(panel.setRegion){
38105             panel.setRegion(this);
38106         }
38107         this.panels.add(panel);
38108         el.setStyle("position", "absolute");
38109         if(!panel.background){
38110             this.setActivePanel(panel);
38111             if(this.config.initialSize && this.panels.getCount()==1){
38112                 this.resizeTo(this.config.initialSize);
38113             }
38114         }
38115         this.fireEvent("paneladded", this, panel);
38116         return panel;
38117     },
38118     
38119     /**
38120      * Returns true if the panel is in this region.
38121      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38122      * @return {Boolean}
38123      */
38124     hasPanel : function(panel){
38125         if(typeof panel == "object"){ // must be panel obj
38126             panel = panel.getId();
38127         }
38128         return this.getPanel(panel) ? true : false;
38129     },
38130     
38131     /**
38132      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38133      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38134      * @param {Boolean} preservePanel Overrides the config preservePanel option
38135      * @return {Roo.ContentPanel} The panel that was removed
38136      */
38137     remove : function(panel, preservePanel){
38138         panel = this.getPanel(panel);
38139         if(!panel){
38140             return null;
38141         }
38142         var e = {};
38143         this.fireEvent("beforeremove", this, panel, e);
38144         if(e.cancel === true){
38145             return null;
38146         }
38147         var panelId = panel.getId();
38148         this.panels.removeKey(panelId);
38149         return panel;
38150     },
38151     
38152     /**
38153      * Returns the panel specified or null if it's not in this region.
38154      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38155      * @return {Roo.ContentPanel}
38156      */
38157     getPanel : function(id){
38158         if(typeof id == "object"){ // must be panel obj
38159             return id;
38160         }
38161         return this.panels.get(id);
38162     },
38163     
38164     /**
38165      * Returns this regions position (north/south/east/west/center).
38166      * @return {String} 
38167      */
38168     getPosition: function(){
38169         return this.position;    
38170     }
38171 });/*
38172  * Based on:
38173  * Ext JS Library 1.1.1
38174  * Copyright(c) 2006-2007, Ext JS, LLC.
38175  *
38176  * Originally Released Under LGPL - original licence link has changed is not relivant.
38177  *
38178  * Fork - LGPL
38179  * <script type="text/javascript">
38180  */
38181  
38182 /**
38183  * @class Roo.bootstrap.layout.Region
38184  * @extends Roo.bootstrap.layout.Basic
38185  * This class represents a region in a layout manager.
38186  
38187  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38188  * @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})
38189  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38190  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38191  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38192  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38193  * @cfg {String}    title           The title for the region (overrides panel titles)
38194  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38195  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38196  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38197  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38198  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38199  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38200  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38201  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38202  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38203  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38204
38205  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38206  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38207  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38208  * @cfg {Number}    width           For East/West panels
38209  * @cfg {Number}    height          For North/South panels
38210  * @cfg {Boolean}   split           To show the splitter
38211  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38212  * 
38213  * @cfg {string}   cls             Extra CSS classes to add to region
38214  * 
38215  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38216  * @cfg {string}   region  the region that it inhabits..
38217  *
38218
38219  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38220  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38221
38222  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38223  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38224  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38225  */
38226 Roo.bootstrap.layout.Region = function(config)
38227 {
38228     this.applyConfig(config);
38229
38230     var mgr = config.mgr;
38231     var pos = config.region;
38232     config.skipConfig = true;
38233     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38234     
38235     if (mgr.el) {
38236         this.onRender(mgr.el);   
38237     }
38238      
38239     this.visible = true;
38240     this.collapsed = false;
38241     this.unrendered_panels = [];
38242 };
38243
38244 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38245
38246     position: '', // set by wrapper (eg. north/south etc..)
38247     unrendered_panels : null,  // unrendered panels.
38248     
38249     tabPosition : false,
38250     
38251     mgr: false, // points to 'Border'
38252     
38253     
38254     createBody : function(){
38255         /** This region's body element 
38256         * @type Roo.Element */
38257         this.bodyEl = this.el.createChild({
38258                 tag: "div",
38259                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38260         });
38261     },
38262
38263     onRender: function(ctr, pos)
38264     {
38265         var dh = Roo.DomHelper;
38266         /** This region's container element 
38267         * @type Roo.Element */
38268         this.el = dh.append(ctr.dom, {
38269                 tag: "div",
38270                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38271             }, true);
38272         /** This region's title element 
38273         * @type Roo.Element */
38274     
38275         this.titleEl = dh.append(this.el.dom,  {
38276                 tag: "div",
38277                 unselectable: "on",
38278                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38279                 children:[
38280                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38281                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38282                 ]
38283             }, true);
38284         
38285         this.titleEl.enableDisplayMode();
38286         /** This region's title text element 
38287         * @type HTMLElement */
38288         this.titleTextEl = this.titleEl.dom.firstChild;
38289         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38290         /*
38291         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38292         this.closeBtn.enableDisplayMode();
38293         this.closeBtn.on("click", this.closeClicked, this);
38294         this.closeBtn.hide();
38295     */
38296         this.createBody(this.config);
38297         if(this.config.hideWhenEmpty){
38298             this.hide();
38299             this.on("paneladded", this.validateVisibility, this);
38300             this.on("panelremoved", this.validateVisibility, this);
38301         }
38302         if(this.autoScroll){
38303             this.bodyEl.setStyle("overflow", "auto");
38304         }else{
38305             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38306         }
38307         //if(c.titlebar !== false){
38308             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38309                 this.titleEl.hide();
38310             }else{
38311                 this.titleEl.show();
38312                 if(this.config.title){
38313                     this.titleTextEl.innerHTML = this.config.title;
38314                 }
38315             }
38316         //}
38317         if(this.config.collapsed){
38318             this.collapse(true);
38319         }
38320         if(this.config.hidden){
38321             this.hide();
38322         }
38323         
38324         if (this.unrendered_panels && this.unrendered_panels.length) {
38325             for (var i =0;i< this.unrendered_panels.length; i++) {
38326                 this.add(this.unrendered_panels[i]);
38327             }
38328             this.unrendered_panels = null;
38329             
38330         }
38331         
38332     },
38333     
38334     applyConfig : function(c)
38335     {
38336         /*
38337          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38338             var dh = Roo.DomHelper;
38339             if(c.titlebar !== false){
38340                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38341                 this.collapseBtn.on("click", this.collapse, this);
38342                 this.collapseBtn.enableDisplayMode();
38343                 /*
38344                 if(c.showPin === true || this.showPin){
38345                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38346                     this.stickBtn.enableDisplayMode();
38347                     this.stickBtn.on("click", this.expand, this);
38348                     this.stickBtn.hide();
38349                 }
38350                 
38351             }
38352             */
38353             /** This region's collapsed element
38354             * @type Roo.Element */
38355             /*
38356              *
38357             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38358                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38359             ]}, true);
38360             
38361             if(c.floatable !== false){
38362                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38363                this.collapsedEl.on("click", this.collapseClick, this);
38364             }
38365
38366             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38367                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38368                    id: "message", unselectable: "on", style:{"float":"left"}});
38369                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38370              }
38371             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38372             this.expandBtn.on("click", this.expand, this);
38373             
38374         }
38375         
38376         if(this.collapseBtn){
38377             this.collapseBtn.setVisible(c.collapsible == true);
38378         }
38379         
38380         this.cmargins = c.cmargins || this.cmargins ||
38381                          (this.position == "west" || this.position == "east" ?
38382                              {top: 0, left: 2, right:2, bottom: 0} :
38383                              {top: 2, left: 0, right:0, bottom: 2});
38384         */
38385         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38386         
38387         
38388         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38389         
38390         this.autoScroll = c.autoScroll || false;
38391         
38392         
38393        
38394         
38395         this.duration = c.duration || .30;
38396         this.slideDuration = c.slideDuration || .45;
38397         this.config = c;
38398        
38399     },
38400     /**
38401      * Returns true if this region is currently visible.
38402      * @return {Boolean}
38403      */
38404     isVisible : function(){
38405         return this.visible;
38406     },
38407
38408     /**
38409      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38410      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38411      */
38412     //setCollapsedTitle : function(title){
38413     //    title = title || "&#160;";
38414      //   if(this.collapsedTitleTextEl){
38415       //      this.collapsedTitleTextEl.innerHTML = title;
38416        // }
38417     //},
38418
38419     getBox : function(){
38420         var b;
38421       //  if(!this.collapsed){
38422             b = this.el.getBox(false, true);
38423        // }else{
38424           //  b = this.collapsedEl.getBox(false, true);
38425         //}
38426         return b;
38427     },
38428
38429     getMargins : function(){
38430         return this.margins;
38431         //return this.collapsed ? this.cmargins : this.margins;
38432     },
38433 /*
38434     highlight : function(){
38435         this.el.addClass("x-layout-panel-dragover");
38436     },
38437
38438     unhighlight : function(){
38439         this.el.removeClass("x-layout-panel-dragover");
38440     },
38441 */
38442     updateBox : function(box)
38443     {
38444         if (!this.bodyEl) {
38445             return; // not rendered yet..
38446         }
38447         
38448         this.box = box;
38449         if(!this.collapsed){
38450             this.el.dom.style.left = box.x + "px";
38451             this.el.dom.style.top = box.y + "px";
38452             this.updateBody(box.width, box.height);
38453         }else{
38454             this.collapsedEl.dom.style.left = box.x + "px";
38455             this.collapsedEl.dom.style.top = box.y + "px";
38456             this.collapsedEl.setSize(box.width, box.height);
38457         }
38458         if(this.tabs){
38459             this.tabs.autoSizeTabs();
38460         }
38461     },
38462
38463     updateBody : function(w, h)
38464     {
38465         if(w !== null){
38466             this.el.setWidth(w);
38467             w -= this.el.getBorderWidth("rl");
38468             if(this.config.adjustments){
38469                 w += this.config.adjustments[0];
38470             }
38471         }
38472         if(h !== null && h > 0){
38473             this.el.setHeight(h);
38474             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38475             h -= this.el.getBorderWidth("tb");
38476             if(this.config.adjustments){
38477                 h += this.config.adjustments[1];
38478             }
38479             this.bodyEl.setHeight(h);
38480             if(this.tabs){
38481                 h = this.tabs.syncHeight(h);
38482             }
38483         }
38484         if(this.panelSize){
38485             w = w !== null ? w : this.panelSize.width;
38486             h = h !== null ? h : this.panelSize.height;
38487         }
38488         if(this.activePanel){
38489             var el = this.activePanel.getEl();
38490             w = w !== null ? w : el.getWidth();
38491             h = h !== null ? h : el.getHeight();
38492             this.panelSize = {width: w, height: h};
38493             this.activePanel.setSize(w, h);
38494         }
38495         if(Roo.isIE && this.tabs){
38496             this.tabs.el.repaint();
38497         }
38498     },
38499
38500     /**
38501      * Returns the container element for this region.
38502      * @return {Roo.Element}
38503      */
38504     getEl : function(){
38505         return this.el;
38506     },
38507
38508     /**
38509      * Hides this region.
38510      */
38511     hide : function(){
38512         //if(!this.collapsed){
38513             this.el.dom.style.left = "-2000px";
38514             this.el.hide();
38515         //}else{
38516          //   this.collapsedEl.dom.style.left = "-2000px";
38517          //   this.collapsedEl.hide();
38518        // }
38519         this.visible = false;
38520         this.fireEvent("visibilitychange", this, false);
38521     },
38522
38523     /**
38524      * Shows this region if it was previously hidden.
38525      */
38526     show : function(){
38527         //if(!this.collapsed){
38528             this.el.show();
38529         //}else{
38530         //    this.collapsedEl.show();
38531        // }
38532         this.visible = true;
38533         this.fireEvent("visibilitychange", this, true);
38534     },
38535 /*
38536     closeClicked : function(){
38537         if(this.activePanel){
38538             this.remove(this.activePanel);
38539         }
38540     },
38541
38542     collapseClick : function(e){
38543         if(this.isSlid){
38544            e.stopPropagation();
38545            this.slideIn();
38546         }else{
38547            e.stopPropagation();
38548            this.slideOut();
38549         }
38550     },
38551 */
38552     /**
38553      * Collapses this region.
38554      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38555      */
38556     /*
38557     collapse : function(skipAnim, skipCheck = false){
38558         if(this.collapsed) {
38559             return;
38560         }
38561         
38562         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38563             
38564             this.collapsed = true;
38565             if(this.split){
38566                 this.split.el.hide();
38567             }
38568             if(this.config.animate && skipAnim !== true){
38569                 this.fireEvent("invalidated", this);
38570                 this.animateCollapse();
38571             }else{
38572                 this.el.setLocation(-20000,-20000);
38573                 this.el.hide();
38574                 this.collapsedEl.show();
38575                 this.fireEvent("collapsed", this);
38576                 this.fireEvent("invalidated", this);
38577             }
38578         }
38579         
38580     },
38581 */
38582     animateCollapse : function(){
38583         // overridden
38584     },
38585
38586     /**
38587      * Expands this region if it was previously collapsed.
38588      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38589      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38590      */
38591     /*
38592     expand : function(e, skipAnim){
38593         if(e) {
38594             e.stopPropagation();
38595         }
38596         if(!this.collapsed || this.el.hasActiveFx()) {
38597             return;
38598         }
38599         if(this.isSlid){
38600             this.afterSlideIn();
38601             skipAnim = true;
38602         }
38603         this.collapsed = false;
38604         if(this.config.animate && skipAnim !== true){
38605             this.animateExpand();
38606         }else{
38607             this.el.show();
38608             if(this.split){
38609                 this.split.el.show();
38610             }
38611             this.collapsedEl.setLocation(-2000,-2000);
38612             this.collapsedEl.hide();
38613             this.fireEvent("invalidated", this);
38614             this.fireEvent("expanded", this);
38615         }
38616     },
38617 */
38618     animateExpand : function(){
38619         // overridden
38620     },
38621
38622     initTabs : function()
38623     {
38624         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38625         
38626         var ts = new Roo.bootstrap.panel.Tabs({
38627             el: this.bodyEl.dom,
38628             region : this,
38629             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38630             disableTooltips: this.config.disableTabTips,
38631             toolbar : this.config.toolbar
38632         });
38633         
38634         if(this.config.hideTabs){
38635             ts.stripWrap.setDisplayed(false);
38636         }
38637         this.tabs = ts;
38638         ts.resizeTabs = this.config.resizeTabs === true;
38639         ts.minTabWidth = this.config.minTabWidth || 40;
38640         ts.maxTabWidth = this.config.maxTabWidth || 250;
38641         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38642         ts.monitorResize = false;
38643         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38644         ts.bodyEl.addClass('roo-layout-tabs-body');
38645         this.panels.each(this.initPanelAsTab, this);
38646     },
38647
38648     initPanelAsTab : function(panel){
38649         var ti = this.tabs.addTab(
38650             panel.getEl().id,
38651             panel.getTitle(),
38652             null,
38653             this.config.closeOnTab && panel.isClosable(),
38654             panel.tpl
38655         );
38656         if(panel.tabTip !== undefined){
38657             ti.setTooltip(panel.tabTip);
38658         }
38659         ti.on("activate", function(){
38660               this.setActivePanel(panel);
38661         }, this);
38662         
38663         if(this.config.closeOnTab){
38664             ti.on("beforeclose", function(t, e){
38665                 e.cancel = true;
38666                 this.remove(panel);
38667             }, this);
38668         }
38669         
38670         panel.tabItem = ti;
38671         
38672         return ti;
38673     },
38674
38675     updatePanelTitle : function(panel, title)
38676     {
38677         if(this.activePanel == panel){
38678             this.updateTitle(title);
38679         }
38680         if(this.tabs){
38681             var ti = this.tabs.getTab(panel.getEl().id);
38682             ti.setText(title);
38683             if(panel.tabTip !== undefined){
38684                 ti.setTooltip(panel.tabTip);
38685             }
38686         }
38687     },
38688
38689     updateTitle : function(title){
38690         if(this.titleTextEl && !this.config.title){
38691             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38692         }
38693     },
38694
38695     setActivePanel : function(panel)
38696     {
38697         panel = this.getPanel(panel);
38698         if(this.activePanel && this.activePanel != panel){
38699             if(this.activePanel.setActiveState(false) === false){
38700                 return;
38701             }
38702         }
38703         this.activePanel = panel;
38704         panel.setActiveState(true);
38705         if(this.panelSize){
38706             panel.setSize(this.panelSize.width, this.panelSize.height);
38707         }
38708         if(this.closeBtn){
38709             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38710         }
38711         this.updateTitle(panel.getTitle());
38712         if(this.tabs){
38713             this.fireEvent("invalidated", this);
38714         }
38715         this.fireEvent("panelactivated", this, panel);
38716     },
38717
38718     /**
38719      * Shows the specified panel.
38720      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38721      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38722      */
38723     showPanel : function(panel)
38724     {
38725         panel = this.getPanel(panel);
38726         if(panel){
38727             if(this.tabs){
38728                 var tab = this.tabs.getTab(panel.getEl().id);
38729                 if(tab.isHidden()){
38730                     this.tabs.unhideTab(tab.id);
38731                 }
38732                 tab.activate();
38733             }else{
38734                 this.setActivePanel(panel);
38735             }
38736         }
38737         return panel;
38738     },
38739
38740     /**
38741      * Get the active panel for this region.
38742      * @return {Roo.ContentPanel} The active panel or null
38743      */
38744     getActivePanel : function(){
38745         return this.activePanel;
38746     },
38747
38748     validateVisibility : function(){
38749         if(this.panels.getCount() < 1){
38750             this.updateTitle("&#160;");
38751             this.closeBtn.hide();
38752             this.hide();
38753         }else{
38754             if(!this.isVisible()){
38755                 this.show();
38756             }
38757         }
38758     },
38759
38760     /**
38761      * Adds the passed ContentPanel(s) to this region.
38762      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38763      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38764      */
38765     add : function(panel)
38766     {
38767         if(arguments.length > 1){
38768             for(var i = 0, len = arguments.length; i < len; i++) {
38769                 this.add(arguments[i]);
38770             }
38771             return null;
38772         }
38773         
38774         // if we have not been rendered yet, then we can not really do much of this..
38775         if (!this.bodyEl) {
38776             this.unrendered_panels.push(panel);
38777             return panel;
38778         }
38779         
38780         
38781         
38782         
38783         if(this.hasPanel(panel)){
38784             this.showPanel(panel);
38785             return panel;
38786         }
38787         panel.setRegion(this);
38788         this.panels.add(panel);
38789        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38790             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38791             // and hide them... ???
38792             this.bodyEl.dom.appendChild(panel.getEl().dom);
38793             if(panel.background !== true){
38794                 this.setActivePanel(panel);
38795             }
38796             this.fireEvent("paneladded", this, panel);
38797             return panel;
38798         }
38799         */
38800         if(!this.tabs){
38801             this.initTabs();
38802         }else{
38803             this.initPanelAsTab(panel);
38804         }
38805         
38806         
38807         if(panel.background !== true){
38808             this.tabs.activate(panel.getEl().id);
38809         }
38810         this.fireEvent("paneladded", this, panel);
38811         return panel;
38812     },
38813
38814     /**
38815      * Hides the tab for the specified panel.
38816      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38817      */
38818     hidePanel : function(panel){
38819         if(this.tabs && (panel = this.getPanel(panel))){
38820             this.tabs.hideTab(panel.getEl().id);
38821         }
38822     },
38823
38824     /**
38825      * Unhides the tab for a previously hidden panel.
38826      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38827      */
38828     unhidePanel : function(panel){
38829         if(this.tabs && (panel = this.getPanel(panel))){
38830             this.tabs.unhideTab(panel.getEl().id);
38831         }
38832     },
38833
38834     clearPanels : function(){
38835         while(this.panels.getCount() > 0){
38836              this.remove(this.panels.first());
38837         }
38838     },
38839
38840     /**
38841      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38842      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38843      * @param {Boolean} preservePanel Overrides the config preservePanel option
38844      * @return {Roo.ContentPanel} The panel that was removed
38845      */
38846     remove : function(panel, preservePanel)
38847     {
38848         panel = this.getPanel(panel);
38849         if(!panel){
38850             return null;
38851         }
38852         var e = {};
38853         this.fireEvent("beforeremove", this, panel, e);
38854         if(e.cancel === true){
38855             return null;
38856         }
38857         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38858         var panelId = panel.getId();
38859         this.panels.removeKey(panelId);
38860         if(preservePanel){
38861             document.body.appendChild(panel.getEl().dom);
38862         }
38863         if(this.tabs){
38864             this.tabs.removeTab(panel.getEl().id);
38865         }else if (!preservePanel){
38866             this.bodyEl.dom.removeChild(panel.getEl().dom);
38867         }
38868         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38869             var p = this.panels.first();
38870             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38871             tempEl.appendChild(p.getEl().dom);
38872             this.bodyEl.update("");
38873             this.bodyEl.dom.appendChild(p.getEl().dom);
38874             tempEl = null;
38875             this.updateTitle(p.getTitle());
38876             this.tabs = null;
38877             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38878             this.setActivePanel(p);
38879         }
38880         panel.setRegion(null);
38881         if(this.activePanel == panel){
38882             this.activePanel = null;
38883         }
38884         if(this.config.autoDestroy !== false && preservePanel !== true){
38885             try{panel.destroy();}catch(e){}
38886         }
38887         this.fireEvent("panelremoved", this, panel);
38888         return panel;
38889     },
38890
38891     /**
38892      * Returns the TabPanel component used by this region
38893      * @return {Roo.TabPanel}
38894      */
38895     getTabs : function(){
38896         return this.tabs;
38897     },
38898
38899     createTool : function(parentEl, className){
38900         var btn = Roo.DomHelper.append(parentEl, {
38901             tag: "div",
38902             cls: "x-layout-tools-button",
38903             children: [ {
38904                 tag: "div",
38905                 cls: "roo-layout-tools-button-inner " + className,
38906                 html: "&#160;"
38907             }]
38908         }, true);
38909         btn.addClassOnOver("roo-layout-tools-button-over");
38910         return btn;
38911     }
38912 });/*
38913  * Based on:
38914  * Ext JS Library 1.1.1
38915  * Copyright(c) 2006-2007, Ext JS, LLC.
38916  *
38917  * Originally Released Under LGPL - original licence link has changed is not relivant.
38918  *
38919  * Fork - LGPL
38920  * <script type="text/javascript">
38921  */
38922  
38923
38924
38925 /**
38926  * @class Roo.SplitLayoutRegion
38927  * @extends Roo.LayoutRegion
38928  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38929  */
38930 Roo.bootstrap.layout.Split = function(config){
38931     this.cursor = config.cursor;
38932     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38933 };
38934
38935 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38936 {
38937     splitTip : "Drag to resize.",
38938     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38939     useSplitTips : false,
38940
38941     applyConfig : function(config){
38942         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38943     },
38944     
38945     onRender : function(ctr,pos) {
38946         
38947         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38948         if(!this.config.split){
38949             return;
38950         }
38951         if(!this.split){
38952             
38953             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38954                             tag: "div",
38955                             id: this.el.id + "-split",
38956                             cls: "roo-layout-split roo-layout-split-"+this.position,
38957                             html: "&#160;"
38958             });
38959             /** The SplitBar for this region 
38960             * @type Roo.SplitBar */
38961             // does not exist yet...
38962             Roo.log([this.position, this.orientation]);
38963             
38964             this.split = new Roo.bootstrap.SplitBar({
38965                 dragElement : splitEl,
38966                 resizingElement: this.el,
38967                 orientation : this.orientation
38968             });
38969             
38970             this.split.on("moved", this.onSplitMove, this);
38971             this.split.useShim = this.config.useShim === true;
38972             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38973             if(this.useSplitTips){
38974                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38975             }
38976             //if(config.collapsible){
38977             //    this.split.el.on("dblclick", this.collapse,  this);
38978             //}
38979         }
38980         if(typeof this.config.minSize != "undefined"){
38981             this.split.minSize = this.config.minSize;
38982         }
38983         if(typeof this.config.maxSize != "undefined"){
38984             this.split.maxSize = this.config.maxSize;
38985         }
38986         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38987             this.hideSplitter();
38988         }
38989         
38990     },
38991
38992     getHMaxSize : function(){
38993          var cmax = this.config.maxSize || 10000;
38994          var center = this.mgr.getRegion("center");
38995          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38996     },
38997
38998     getVMaxSize : function(){
38999          var cmax = this.config.maxSize || 10000;
39000          var center = this.mgr.getRegion("center");
39001          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39002     },
39003
39004     onSplitMove : function(split, newSize){
39005         this.fireEvent("resized", this, newSize);
39006     },
39007     
39008     /** 
39009      * Returns the {@link Roo.SplitBar} for this region.
39010      * @return {Roo.SplitBar}
39011      */
39012     getSplitBar : function(){
39013         return this.split;
39014     },
39015     
39016     hide : function(){
39017         this.hideSplitter();
39018         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39019     },
39020
39021     hideSplitter : function(){
39022         if(this.split){
39023             this.split.el.setLocation(-2000,-2000);
39024             this.split.el.hide();
39025         }
39026     },
39027
39028     show : function(){
39029         if(this.split){
39030             this.split.el.show();
39031         }
39032         Roo.bootstrap.layout.Split.superclass.show.call(this);
39033     },
39034     
39035     beforeSlide: function(){
39036         if(Roo.isGecko){// firefox overflow auto bug workaround
39037             this.bodyEl.clip();
39038             if(this.tabs) {
39039                 this.tabs.bodyEl.clip();
39040             }
39041             if(this.activePanel){
39042                 this.activePanel.getEl().clip();
39043                 
39044                 if(this.activePanel.beforeSlide){
39045                     this.activePanel.beforeSlide();
39046                 }
39047             }
39048         }
39049     },
39050     
39051     afterSlide : function(){
39052         if(Roo.isGecko){// firefox overflow auto bug workaround
39053             this.bodyEl.unclip();
39054             if(this.tabs) {
39055                 this.tabs.bodyEl.unclip();
39056             }
39057             if(this.activePanel){
39058                 this.activePanel.getEl().unclip();
39059                 if(this.activePanel.afterSlide){
39060                     this.activePanel.afterSlide();
39061                 }
39062             }
39063         }
39064     },
39065
39066     initAutoHide : function(){
39067         if(this.autoHide !== false){
39068             if(!this.autoHideHd){
39069                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39070                 this.autoHideHd = {
39071                     "mouseout": function(e){
39072                         if(!e.within(this.el, true)){
39073                             st.delay(500);
39074                         }
39075                     },
39076                     "mouseover" : function(e){
39077                         st.cancel();
39078                     },
39079                     scope : this
39080                 };
39081             }
39082             this.el.on(this.autoHideHd);
39083         }
39084     },
39085
39086     clearAutoHide : function(){
39087         if(this.autoHide !== false){
39088             this.el.un("mouseout", this.autoHideHd.mouseout);
39089             this.el.un("mouseover", this.autoHideHd.mouseover);
39090         }
39091     },
39092
39093     clearMonitor : function(){
39094         Roo.get(document).un("click", this.slideInIf, this);
39095     },
39096
39097     // these names are backwards but not changed for compat
39098     slideOut : function(){
39099         if(this.isSlid || this.el.hasActiveFx()){
39100             return;
39101         }
39102         this.isSlid = true;
39103         if(this.collapseBtn){
39104             this.collapseBtn.hide();
39105         }
39106         this.closeBtnState = this.closeBtn.getStyle('display');
39107         this.closeBtn.hide();
39108         if(this.stickBtn){
39109             this.stickBtn.show();
39110         }
39111         this.el.show();
39112         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39113         this.beforeSlide();
39114         this.el.setStyle("z-index", 10001);
39115         this.el.slideIn(this.getSlideAnchor(), {
39116             callback: function(){
39117                 this.afterSlide();
39118                 this.initAutoHide();
39119                 Roo.get(document).on("click", this.slideInIf, this);
39120                 this.fireEvent("slideshow", this);
39121             },
39122             scope: this,
39123             block: true
39124         });
39125     },
39126
39127     afterSlideIn : function(){
39128         this.clearAutoHide();
39129         this.isSlid = false;
39130         this.clearMonitor();
39131         this.el.setStyle("z-index", "");
39132         if(this.collapseBtn){
39133             this.collapseBtn.show();
39134         }
39135         this.closeBtn.setStyle('display', this.closeBtnState);
39136         if(this.stickBtn){
39137             this.stickBtn.hide();
39138         }
39139         this.fireEvent("slidehide", this);
39140     },
39141
39142     slideIn : function(cb){
39143         if(!this.isSlid || this.el.hasActiveFx()){
39144             Roo.callback(cb);
39145             return;
39146         }
39147         this.isSlid = false;
39148         this.beforeSlide();
39149         this.el.slideOut(this.getSlideAnchor(), {
39150             callback: function(){
39151                 this.el.setLeftTop(-10000, -10000);
39152                 this.afterSlide();
39153                 this.afterSlideIn();
39154                 Roo.callback(cb);
39155             },
39156             scope: this,
39157             block: true
39158         });
39159     },
39160     
39161     slideInIf : function(e){
39162         if(!e.within(this.el)){
39163             this.slideIn();
39164         }
39165     },
39166
39167     animateCollapse : function(){
39168         this.beforeSlide();
39169         this.el.setStyle("z-index", 20000);
39170         var anchor = this.getSlideAnchor();
39171         this.el.slideOut(anchor, {
39172             callback : function(){
39173                 this.el.setStyle("z-index", "");
39174                 this.collapsedEl.slideIn(anchor, {duration:.3});
39175                 this.afterSlide();
39176                 this.el.setLocation(-10000,-10000);
39177                 this.el.hide();
39178                 this.fireEvent("collapsed", this);
39179             },
39180             scope: this,
39181             block: true
39182         });
39183     },
39184
39185     animateExpand : function(){
39186         this.beforeSlide();
39187         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39188         this.el.setStyle("z-index", 20000);
39189         this.collapsedEl.hide({
39190             duration:.1
39191         });
39192         this.el.slideIn(this.getSlideAnchor(), {
39193             callback : function(){
39194                 this.el.setStyle("z-index", "");
39195                 this.afterSlide();
39196                 if(this.split){
39197                     this.split.el.show();
39198                 }
39199                 this.fireEvent("invalidated", this);
39200                 this.fireEvent("expanded", this);
39201             },
39202             scope: this,
39203             block: true
39204         });
39205     },
39206
39207     anchors : {
39208         "west" : "left",
39209         "east" : "right",
39210         "north" : "top",
39211         "south" : "bottom"
39212     },
39213
39214     sanchors : {
39215         "west" : "l",
39216         "east" : "r",
39217         "north" : "t",
39218         "south" : "b"
39219     },
39220
39221     canchors : {
39222         "west" : "tl-tr",
39223         "east" : "tr-tl",
39224         "north" : "tl-bl",
39225         "south" : "bl-tl"
39226     },
39227
39228     getAnchor : function(){
39229         return this.anchors[this.position];
39230     },
39231
39232     getCollapseAnchor : function(){
39233         return this.canchors[this.position];
39234     },
39235
39236     getSlideAnchor : function(){
39237         return this.sanchors[this.position];
39238     },
39239
39240     getAlignAdj : function(){
39241         var cm = this.cmargins;
39242         switch(this.position){
39243             case "west":
39244                 return [0, 0];
39245             break;
39246             case "east":
39247                 return [0, 0];
39248             break;
39249             case "north":
39250                 return [0, 0];
39251             break;
39252             case "south":
39253                 return [0, 0];
39254             break;
39255         }
39256     },
39257
39258     getExpandAdj : function(){
39259         var c = this.collapsedEl, cm = this.cmargins;
39260         switch(this.position){
39261             case "west":
39262                 return [-(cm.right+c.getWidth()+cm.left), 0];
39263             break;
39264             case "east":
39265                 return [cm.right+c.getWidth()+cm.left, 0];
39266             break;
39267             case "north":
39268                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39269             break;
39270             case "south":
39271                 return [0, cm.top+cm.bottom+c.getHeight()];
39272             break;
39273         }
39274     }
39275 });/*
39276  * Based on:
39277  * Ext JS Library 1.1.1
39278  * Copyright(c) 2006-2007, Ext JS, LLC.
39279  *
39280  * Originally Released Under LGPL - original licence link has changed is not relivant.
39281  *
39282  * Fork - LGPL
39283  * <script type="text/javascript">
39284  */
39285 /*
39286  * These classes are private internal classes
39287  */
39288 Roo.bootstrap.layout.Center = function(config){
39289     config.region = "center";
39290     Roo.bootstrap.layout.Region.call(this, config);
39291     this.visible = true;
39292     this.minWidth = config.minWidth || 20;
39293     this.minHeight = config.minHeight || 20;
39294 };
39295
39296 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39297     hide : function(){
39298         // center panel can't be hidden
39299     },
39300     
39301     show : function(){
39302         // center panel can't be hidden
39303     },
39304     
39305     getMinWidth: function(){
39306         return this.minWidth;
39307     },
39308     
39309     getMinHeight: function(){
39310         return this.minHeight;
39311     }
39312 });
39313
39314
39315
39316
39317  
39318
39319
39320
39321
39322
39323
39324 Roo.bootstrap.layout.North = function(config)
39325 {
39326     config.region = 'north';
39327     config.cursor = 'n-resize';
39328     
39329     Roo.bootstrap.layout.Split.call(this, config);
39330     
39331     
39332     if(this.split){
39333         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39334         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39335         this.split.el.addClass("roo-layout-split-v");
39336     }
39337     //var size = config.initialSize || config.height;
39338     //if(this.el && typeof size != "undefined"){
39339     //    this.el.setHeight(size);
39340     //}
39341 };
39342 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39343 {
39344     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39345      
39346      
39347     onRender : function(ctr, pos)
39348     {
39349         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39350         var size = this.config.initialSize || this.config.height;
39351         if(this.el && typeof size != "undefined"){
39352             this.el.setHeight(size);
39353         }
39354     
39355     },
39356     
39357     getBox : function(){
39358         if(this.collapsed){
39359             return this.collapsedEl.getBox();
39360         }
39361         var box = this.el.getBox();
39362         if(this.split){
39363             box.height += this.split.el.getHeight();
39364         }
39365         return box;
39366     },
39367     
39368     updateBox : function(box){
39369         if(this.split && !this.collapsed){
39370             box.height -= this.split.el.getHeight();
39371             this.split.el.setLeft(box.x);
39372             this.split.el.setTop(box.y+box.height);
39373             this.split.el.setWidth(box.width);
39374         }
39375         if(this.collapsed){
39376             this.updateBody(box.width, null);
39377         }
39378         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39379     }
39380 });
39381
39382
39383
39384
39385
39386 Roo.bootstrap.layout.South = function(config){
39387     config.region = 'south';
39388     config.cursor = 's-resize';
39389     Roo.bootstrap.layout.Split.call(this, config);
39390     if(this.split){
39391         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39392         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39393         this.split.el.addClass("roo-layout-split-v");
39394     }
39395     
39396 };
39397
39398 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39399     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39400     
39401     onRender : function(ctr, pos)
39402     {
39403         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39404         var size = this.config.initialSize || this.config.height;
39405         if(this.el && typeof size != "undefined"){
39406             this.el.setHeight(size);
39407         }
39408     
39409     },
39410     
39411     getBox : function(){
39412         if(this.collapsed){
39413             return this.collapsedEl.getBox();
39414         }
39415         var box = this.el.getBox();
39416         if(this.split){
39417             var sh = this.split.el.getHeight();
39418             box.height += sh;
39419             box.y -= sh;
39420         }
39421         return box;
39422     },
39423     
39424     updateBox : function(box){
39425         if(this.split && !this.collapsed){
39426             var sh = this.split.el.getHeight();
39427             box.height -= sh;
39428             box.y += sh;
39429             this.split.el.setLeft(box.x);
39430             this.split.el.setTop(box.y-sh);
39431             this.split.el.setWidth(box.width);
39432         }
39433         if(this.collapsed){
39434             this.updateBody(box.width, null);
39435         }
39436         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39437     }
39438 });
39439
39440 Roo.bootstrap.layout.East = function(config){
39441     config.region = "east";
39442     config.cursor = "e-resize";
39443     Roo.bootstrap.layout.Split.call(this, config);
39444     if(this.split){
39445         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39446         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39447         this.split.el.addClass("roo-layout-split-h");
39448     }
39449     
39450 };
39451 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39452     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39453     
39454     onRender : function(ctr, pos)
39455     {
39456         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39457         var size = this.config.initialSize || this.config.width;
39458         if(this.el && typeof size != "undefined"){
39459             this.el.setWidth(size);
39460         }
39461     
39462     },
39463     
39464     getBox : function(){
39465         if(this.collapsed){
39466             return this.collapsedEl.getBox();
39467         }
39468         var box = this.el.getBox();
39469         if(this.split){
39470             var sw = this.split.el.getWidth();
39471             box.width += sw;
39472             box.x -= sw;
39473         }
39474         return box;
39475     },
39476
39477     updateBox : function(box){
39478         if(this.split && !this.collapsed){
39479             var sw = this.split.el.getWidth();
39480             box.width -= sw;
39481             this.split.el.setLeft(box.x);
39482             this.split.el.setTop(box.y);
39483             this.split.el.setHeight(box.height);
39484             box.x += sw;
39485         }
39486         if(this.collapsed){
39487             this.updateBody(null, box.height);
39488         }
39489         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39490     }
39491 });
39492
39493 Roo.bootstrap.layout.West = function(config){
39494     config.region = "west";
39495     config.cursor = "w-resize";
39496     
39497     Roo.bootstrap.layout.Split.call(this, config);
39498     if(this.split){
39499         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39500         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39501         this.split.el.addClass("roo-layout-split-h");
39502     }
39503     
39504 };
39505 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39506     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39507     
39508     onRender: function(ctr, pos)
39509     {
39510         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39511         var size = this.config.initialSize || this.config.width;
39512         if(typeof size != "undefined"){
39513             this.el.setWidth(size);
39514         }
39515     },
39516     
39517     getBox : function(){
39518         if(this.collapsed){
39519             return this.collapsedEl.getBox();
39520         }
39521         var box = this.el.getBox();
39522         if (box.width == 0) {
39523             box.width = this.config.width; // kludge?
39524         }
39525         if(this.split){
39526             box.width += this.split.el.getWidth();
39527         }
39528         return box;
39529     },
39530     
39531     updateBox : function(box){
39532         if(this.split && !this.collapsed){
39533             var sw = this.split.el.getWidth();
39534             box.width -= sw;
39535             this.split.el.setLeft(box.x+box.width);
39536             this.split.el.setTop(box.y);
39537             this.split.el.setHeight(box.height);
39538         }
39539         if(this.collapsed){
39540             this.updateBody(null, box.height);
39541         }
39542         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39543     }
39544 });Roo.namespace("Roo.bootstrap.panel");/*
39545  * Based on:
39546  * Ext JS Library 1.1.1
39547  * Copyright(c) 2006-2007, Ext JS, LLC.
39548  *
39549  * Originally Released Under LGPL - original licence link has changed is not relivant.
39550  *
39551  * Fork - LGPL
39552  * <script type="text/javascript">
39553  */
39554 /**
39555  * @class Roo.ContentPanel
39556  * @extends Roo.util.Observable
39557  * A basic ContentPanel element.
39558  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39559  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39560  * @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
39561  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39562  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39563  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39564  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39565  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39566  * @cfg {String} title          The title for this panel
39567  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39568  * @cfg {String} url            Calls {@link #setUrl} with this value
39569  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39570  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39571  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39572  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39573  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39574  * @cfg {Boolean} badges render the badges
39575  * @cfg {String} cls  extra classes to use  
39576  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39577
39578  * @constructor
39579  * Create a new ContentPanel.
39580  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39581  * @param {String/Object} config A string to set only the title or a config object
39582  * @param {String} content (optional) Set the HTML content for this panel
39583  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39584  */
39585 Roo.bootstrap.panel.Content = function( config){
39586     
39587     this.tpl = config.tpl || false;
39588     
39589     var el = config.el;
39590     var content = config.content;
39591
39592     if(config.autoCreate){ // xtype is available if this is called from factory
39593         el = Roo.id();
39594     }
39595     this.el = Roo.get(el);
39596     if(!this.el && config && config.autoCreate){
39597         if(typeof config.autoCreate == "object"){
39598             if(!config.autoCreate.id){
39599                 config.autoCreate.id = config.id||el;
39600             }
39601             this.el = Roo.DomHelper.append(document.body,
39602                         config.autoCreate, true);
39603         }else{
39604             var elcfg =  {
39605                 tag: "div",
39606                 cls: (config.cls || '') +
39607                     (config.background ? ' bg-' + config.background : '') +
39608                     " roo-layout-inactive-content",
39609                 id: config.id||el
39610             };
39611             if (config.iframe) {
39612                 elcfg.cn = [
39613                     {
39614                         tag : 'iframe',
39615                         style : 'border: 0px',
39616                         src : 'about:blank'
39617                     }
39618                 ];
39619             }
39620               
39621             if (config.html) {
39622                 elcfg.html = config.html;
39623                 
39624             }
39625                         
39626             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39627             if (config.iframe) {
39628                 this.iframeEl = this.el.select('iframe',true).first();
39629             }
39630             
39631         }
39632     } 
39633     this.closable = false;
39634     this.loaded = false;
39635     this.active = false;
39636    
39637       
39638     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39639         
39640         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39641         
39642         this.wrapEl = this.el; //this.el.wrap();
39643         var ti = [];
39644         if (config.toolbar.items) {
39645             ti = config.toolbar.items ;
39646             delete config.toolbar.items ;
39647         }
39648         
39649         var nitems = [];
39650         this.toolbar.render(this.wrapEl, 'before');
39651         for(var i =0;i < ti.length;i++) {
39652           //  Roo.log(['add child', items[i]]);
39653             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39654         }
39655         this.toolbar.items = nitems;
39656         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39657         delete config.toolbar;
39658         
39659     }
39660     /*
39661     // xtype created footer. - not sure if will work as we normally have to render first..
39662     if (this.footer && !this.footer.el && this.footer.xtype) {
39663         if (!this.wrapEl) {
39664             this.wrapEl = this.el.wrap();
39665         }
39666     
39667         this.footer.container = this.wrapEl.createChild();
39668          
39669         this.footer = Roo.factory(this.footer, Roo);
39670         
39671     }
39672     */
39673     
39674      if(typeof config == "string"){
39675         this.title = config;
39676     }else{
39677         Roo.apply(this, config);
39678     }
39679     
39680     if(this.resizeEl){
39681         this.resizeEl = Roo.get(this.resizeEl, true);
39682     }else{
39683         this.resizeEl = this.el;
39684     }
39685     // handle view.xtype
39686     
39687  
39688     
39689     
39690     this.addEvents({
39691         /**
39692          * @event activate
39693          * Fires when this panel is activated. 
39694          * @param {Roo.ContentPanel} this
39695          */
39696         "activate" : true,
39697         /**
39698          * @event deactivate
39699          * Fires when this panel is activated. 
39700          * @param {Roo.ContentPanel} this
39701          */
39702         "deactivate" : true,
39703
39704         /**
39705          * @event resize
39706          * Fires when this panel is resized if fitToFrame is true.
39707          * @param {Roo.ContentPanel} this
39708          * @param {Number} width The width after any component adjustments
39709          * @param {Number} height The height after any component adjustments
39710          */
39711         "resize" : true,
39712         
39713          /**
39714          * @event render
39715          * Fires when this tab is created
39716          * @param {Roo.ContentPanel} this
39717          */
39718         "render" : true
39719         
39720         
39721         
39722     });
39723     
39724
39725     
39726     
39727     if(this.autoScroll && !this.iframe){
39728         this.resizeEl.setStyle("overflow", "auto");
39729     } else {
39730         // fix randome scrolling
39731         //this.el.on('scroll', function() {
39732         //    Roo.log('fix random scolling');
39733         //    this.scrollTo('top',0); 
39734         //});
39735     }
39736     content = content || this.content;
39737     if(content){
39738         this.setContent(content);
39739     }
39740     if(config && config.url){
39741         this.setUrl(this.url, this.params, this.loadOnce);
39742     }
39743     
39744     
39745     
39746     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39747     
39748     if (this.view && typeof(this.view.xtype) != 'undefined') {
39749         this.view.el = this.el.appendChild(document.createElement("div"));
39750         this.view = Roo.factory(this.view); 
39751         this.view.render  &&  this.view.render(false, '');  
39752     }
39753     
39754     
39755     this.fireEvent('render', this);
39756 };
39757
39758 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39759     
39760     cls : '',
39761     background : '',
39762     
39763     tabTip : '',
39764     
39765     iframe : false,
39766     iframeEl : false,
39767     
39768     setRegion : function(region){
39769         this.region = region;
39770         this.setActiveClass(region && !this.background);
39771     },
39772     
39773     
39774     setActiveClass: function(state)
39775     {
39776         if(state){
39777            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39778            this.el.setStyle('position','relative');
39779         }else{
39780            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39781            this.el.setStyle('position', 'absolute');
39782         } 
39783     },
39784     
39785     /**
39786      * Returns the toolbar for this Panel if one was configured. 
39787      * @return {Roo.Toolbar} 
39788      */
39789     getToolbar : function(){
39790         return this.toolbar;
39791     },
39792     
39793     setActiveState : function(active)
39794     {
39795         this.active = active;
39796         this.setActiveClass(active);
39797         if(!active){
39798             if(this.fireEvent("deactivate", this) === false){
39799                 return false;
39800             }
39801             return true;
39802         }
39803         this.fireEvent("activate", this);
39804         return true;
39805     },
39806     /**
39807      * Updates this panel's element (not for iframe)
39808      * @param {String} content The new content
39809      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39810     */
39811     setContent : function(content, loadScripts){
39812         if (this.iframe) {
39813             return;
39814         }
39815         
39816         this.el.update(content, loadScripts);
39817     },
39818
39819     ignoreResize : function(w, h){
39820         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39821             return true;
39822         }else{
39823             this.lastSize = {width: w, height: h};
39824             return false;
39825         }
39826     },
39827     /**
39828      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39829      * @return {Roo.UpdateManager} The UpdateManager
39830      */
39831     getUpdateManager : function(){
39832         if (this.iframe) {
39833             return false;
39834         }
39835         return this.el.getUpdateManager();
39836     },
39837      /**
39838      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39839      * Does not work with IFRAME contents
39840      * @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:
39841 <pre><code>
39842 panel.load({
39843     url: "your-url.php",
39844     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39845     callback: yourFunction,
39846     scope: yourObject, //(optional scope)
39847     discardUrl: false,
39848     nocache: false,
39849     text: "Loading...",
39850     timeout: 30,
39851     scripts: false
39852 });
39853 </code></pre>
39854      
39855      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39856      * 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.
39857      * @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}
39858      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39859      * @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.
39860      * @return {Roo.ContentPanel} this
39861      */
39862     load : function(){
39863         
39864         if (this.iframe) {
39865             return this;
39866         }
39867         
39868         var um = this.el.getUpdateManager();
39869         um.update.apply(um, arguments);
39870         return this;
39871     },
39872
39873
39874     /**
39875      * 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.
39876      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39877      * @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)
39878      * @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)
39879      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39880      */
39881     setUrl : function(url, params, loadOnce){
39882         if (this.iframe) {
39883             this.iframeEl.dom.src = url;
39884             return false;
39885         }
39886         
39887         if(this.refreshDelegate){
39888             this.removeListener("activate", this.refreshDelegate);
39889         }
39890         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39891         this.on("activate", this.refreshDelegate);
39892         return this.el.getUpdateManager();
39893     },
39894     
39895     _handleRefresh : function(url, params, loadOnce){
39896         if(!loadOnce || !this.loaded){
39897             var updater = this.el.getUpdateManager();
39898             updater.update(url, params, this._setLoaded.createDelegate(this));
39899         }
39900     },
39901     
39902     _setLoaded : function(){
39903         this.loaded = true;
39904     }, 
39905     
39906     /**
39907      * Returns this panel's id
39908      * @return {String} 
39909      */
39910     getId : function(){
39911         return this.el.id;
39912     },
39913     
39914     /** 
39915      * Returns this panel's element - used by regiosn to add.
39916      * @return {Roo.Element} 
39917      */
39918     getEl : function(){
39919         return this.wrapEl || this.el;
39920     },
39921     
39922    
39923     
39924     adjustForComponents : function(width, height)
39925     {
39926         //Roo.log('adjustForComponents ');
39927         if(this.resizeEl != this.el){
39928             width -= this.el.getFrameWidth('lr');
39929             height -= this.el.getFrameWidth('tb');
39930         }
39931         if(this.toolbar){
39932             var te = this.toolbar.getEl();
39933             te.setWidth(width);
39934             height -= te.getHeight();
39935         }
39936         if(this.footer){
39937             var te = this.footer.getEl();
39938             te.setWidth(width);
39939             height -= te.getHeight();
39940         }
39941         
39942         
39943         if(this.adjustments){
39944             width += this.adjustments[0];
39945             height += this.adjustments[1];
39946         }
39947         return {"width": width, "height": height};
39948     },
39949     
39950     setSize : function(width, height){
39951         if(this.fitToFrame && !this.ignoreResize(width, height)){
39952             if(this.fitContainer && this.resizeEl != this.el){
39953                 this.el.setSize(width, height);
39954             }
39955             var size = this.adjustForComponents(width, height);
39956             if (this.iframe) {
39957                 this.iframeEl.setSize(width,height);
39958             }
39959             
39960             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39961             this.fireEvent('resize', this, size.width, size.height);
39962             
39963             
39964         }
39965     },
39966     
39967     /**
39968      * Returns this panel's title
39969      * @return {String} 
39970      */
39971     getTitle : function(){
39972         
39973         if (typeof(this.title) != 'object') {
39974             return this.title;
39975         }
39976         
39977         var t = '';
39978         for (var k in this.title) {
39979             if (!this.title.hasOwnProperty(k)) {
39980                 continue;
39981             }
39982             
39983             if (k.indexOf('-') >= 0) {
39984                 var s = k.split('-');
39985                 for (var i = 0; i<s.length; i++) {
39986                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39987                 }
39988             } else {
39989                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39990             }
39991         }
39992         return t;
39993     },
39994     
39995     /**
39996      * Set this panel's title
39997      * @param {String} title
39998      */
39999     setTitle : function(title){
40000         this.title = title;
40001         if(this.region){
40002             this.region.updatePanelTitle(this, title);
40003         }
40004     },
40005     
40006     /**
40007      * Returns true is this panel was configured to be closable
40008      * @return {Boolean} 
40009      */
40010     isClosable : function(){
40011         return this.closable;
40012     },
40013     
40014     beforeSlide : function(){
40015         this.el.clip();
40016         this.resizeEl.clip();
40017     },
40018     
40019     afterSlide : function(){
40020         this.el.unclip();
40021         this.resizeEl.unclip();
40022     },
40023     
40024     /**
40025      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40026      *   Will fail silently if the {@link #setUrl} method has not been called.
40027      *   This does not activate the panel, just updates its content.
40028      */
40029     refresh : function(){
40030         if(this.refreshDelegate){
40031            this.loaded = false;
40032            this.refreshDelegate();
40033         }
40034     },
40035     
40036     /**
40037      * Destroys this panel
40038      */
40039     destroy : function(){
40040         this.el.removeAllListeners();
40041         var tempEl = document.createElement("span");
40042         tempEl.appendChild(this.el.dom);
40043         tempEl.innerHTML = "";
40044         this.el.remove();
40045         this.el = null;
40046     },
40047     
40048     /**
40049      * form - if the content panel contains a form - this is a reference to it.
40050      * @type {Roo.form.Form}
40051      */
40052     form : false,
40053     /**
40054      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40055      *    This contains a reference to it.
40056      * @type {Roo.View}
40057      */
40058     view : false,
40059     
40060       /**
40061      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40062      * <pre><code>
40063
40064 layout.addxtype({
40065        xtype : 'Form',
40066        items: [ .... ]
40067    }
40068 );
40069
40070 </code></pre>
40071      * @param {Object} cfg Xtype definition of item to add.
40072      */
40073     
40074     
40075     getChildContainer: function () {
40076         return this.getEl();
40077     }
40078     
40079     
40080     /*
40081         var  ret = new Roo.factory(cfg);
40082         return ret;
40083         
40084         
40085         // add form..
40086         if (cfg.xtype.match(/^Form$/)) {
40087             
40088             var el;
40089             //if (this.footer) {
40090             //    el = this.footer.container.insertSibling(false, 'before');
40091             //} else {
40092                 el = this.el.createChild();
40093             //}
40094
40095             this.form = new  Roo.form.Form(cfg);
40096             
40097             
40098             if ( this.form.allItems.length) {
40099                 this.form.render(el.dom);
40100             }
40101             return this.form;
40102         }
40103         // should only have one of theses..
40104         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40105             // views.. should not be just added - used named prop 'view''
40106             
40107             cfg.el = this.el.appendChild(document.createElement("div"));
40108             // factory?
40109             
40110             var ret = new Roo.factory(cfg);
40111              
40112              ret.render && ret.render(false, ''); // render blank..
40113             this.view = ret;
40114             return ret;
40115         }
40116         return false;
40117     }
40118     \*/
40119 });
40120  
40121 /**
40122  * @class Roo.bootstrap.panel.Grid
40123  * @extends Roo.bootstrap.panel.Content
40124  * @constructor
40125  * Create a new GridPanel.
40126  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40127  * @param {Object} config A the config object
40128   
40129  */
40130
40131
40132
40133 Roo.bootstrap.panel.Grid = function(config)
40134 {
40135     
40136       
40137     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40138         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40139
40140     config.el = this.wrapper;
40141     //this.el = this.wrapper;
40142     
40143       if (config.container) {
40144         // ctor'ed from a Border/panel.grid
40145         
40146         
40147         this.wrapper.setStyle("overflow", "hidden");
40148         this.wrapper.addClass('roo-grid-container');
40149
40150     }
40151     
40152     
40153     if(config.toolbar){
40154         var tool_el = this.wrapper.createChild();    
40155         this.toolbar = Roo.factory(config.toolbar);
40156         var ti = [];
40157         if (config.toolbar.items) {
40158             ti = config.toolbar.items ;
40159             delete config.toolbar.items ;
40160         }
40161         
40162         var nitems = [];
40163         this.toolbar.render(tool_el);
40164         for(var i =0;i < ti.length;i++) {
40165           //  Roo.log(['add child', items[i]]);
40166             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40167         }
40168         this.toolbar.items = nitems;
40169         
40170         delete config.toolbar;
40171     }
40172     
40173     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40174     config.grid.scrollBody = true;;
40175     config.grid.monitorWindowResize = false; // turn off autosizing
40176     config.grid.autoHeight = false;
40177     config.grid.autoWidth = false;
40178     
40179     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40180     
40181     if (config.background) {
40182         // render grid on panel activation (if panel background)
40183         this.on('activate', function(gp) {
40184             if (!gp.grid.rendered) {
40185                 gp.grid.render(this.wrapper);
40186                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40187             }
40188         });
40189             
40190     } else {
40191         this.grid.render(this.wrapper);
40192         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40193
40194     }
40195     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40196     // ??? needed ??? config.el = this.wrapper;
40197     
40198     
40199     
40200   
40201     // xtype created footer. - not sure if will work as we normally have to render first..
40202     if (this.footer && !this.footer.el && this.footer.xtype) {
40203         
40204         var ctr = this.grid.getView().getFooterPanel(true);
40205         this.footer.dataSource = this.grid.dataSource;
40206         this.footer = Roo.factory(this.footer, Roo);
40207         this.footer.render(ctr);
40208         
40209     }
40210     
40211     
40212     
40213     
40214      
40215 };
40216
40217 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40218     getId : function(){
40219         return this.grid.id;
40220     },
40221     
40222     /**
40223      * Returns the grid for this panel
40224      * @return {Roo.bootstrap.Table} 
40225      */
40226     getGrid : function(){
40227         return this.grid;    
40228     },
40229     
40230     setSize : function(width, height){
40231         if(!this.ignoreResize(width, height)){
40232             var grid = this.grid;
40233             var size = this.adjustForComponents(width, height);
40234             // tfoot is not a footer?
40235           
40236             
40237             var gridel = grid.getGridEl();
40238             gridel.setSize(size.width, size.height);
40239             
40240             var tbd = grid.getGridEl().select('tbody', true).first();
40241             var thd = grid.getGridEl().select('thead',true).first();
40242             var tbf= grid.getGridEl().select('tfoot', true).first();
40243
40244             if (tbf) {
40245                 size.height -= tbf.getHeight();
40246             }
40247             if (thd) {
40248                 size.height -= thd.getHeight();
40249             }
40250             
40251             tbd.setSize(size.width, size.height );
40252             // this is for the account management tab -seems to work there.
40253             var thd = grid.getGridEl().select('thead',true).first();
40254             //if (tbd) {
40255             //    tbd.setSize(size.width, size.height - thd.getHeight());
40256             //}
40257              
40258             grid.autoSize();
40259         }
40260     },
40261      
40262     
40263     
40264     beforeSlide : function(){
40265         this.grid.getView().scroller.clip();
40266     },
40267     
40268     afterSlide : function(){
40269         this.grid.getView().scroller.unclip();
40270     },
40271     
40272     destroy : function(){
40273         this.grid.destroy();
40274         delete this.grid;
40275         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40276     }
40277 });
40278
40279 /**
40280  * @class Roo.bootstrap.panel.Nest
40281  * @extends Roo.bootstrap.panel.Content
40282  * @constructor
40283  * Create a new Panel, that can contain a layout.Border.
40284  * 
40285  * 
40286  * @param {Roo.BorderLayout} layout The layout for this panel
40287  * @param {String/Object} config A string to set only the title or a config object
40288  */
40289 Roo.bootstrap.panel.Nest = function(config)
40290 {
40291     // construct with only one argument..
40292     /* FIXME - implement nicer consturctors
40293     if (layout.layout) {
40294         config = layout;
40295         layout = config.layout;
40296         delete config.layout;
40297     }
40298     if (layout.xtype && !layout.getEl) {
40299         // then layout needs constructing..
40300         layout = Roo.factory(layout, Roo);
40301     }
40302     */
40303     
40304     config.el =  config.layout.getEl();
40305     
40306     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40307     
40308     config.layout.monitorWindowResize = false; // turn off autosizing
40309     this.layout = config.layout;
40310     this.layout.getEl().addClass("roo-layout-nested-layout");
40311     this.layout.parent = this;
40312     
40313     
40314     
40315     
40316 };
40317
40318 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40319
40320     setSize : function(width, height){
40321         if(!this.ignoreResize(width, height)){
40322             var size = this.adjustForComponents(width, height);
40323             var el = this.layout.getEl();
40324             if (size.height < 1) {
40325                 el.setWidth(size.width);   
40326             } else {
40327                 el.setSize(size.width, size.height);
40328             }
40329             var touch = el.dom.offsetWidth;
40330             this.layout.layout();
40331             // ie requires a double layout on the first pass
40332             if(Roo.isIE && !this.initialized){
40333                 this.initialized = true;
40334                 this.layout.layout();
40335             }
40336         }
40337     },
40338     
40339     // activate all subpanels if not currently active..
40340     
40341     setActiveState : function(active){
40342         this.active = active;
40343         this.setActiveClass(active);
40344         
40345         if(!active){
40346             this.fireEvent("deactivate", this);
40347             return;
40348         }
40349         
40350         this.fireEvent("activate", this);
40351         // not sure if this should happen before or after..
40352         if (!this.layout) {
40353             return; // should not happen..
40354         }
40355         var reg = false;
40356         for (var r in this.layout.regions) {
40357             reg = this.layout.getRegion(r);
40358             if (reg.getActivePanel()) {
40359                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40360                 reg.setActivePanel(reg.getActivePanel());
40361                 continue;
40362             }
40363             if (!reg.panels.length) {
40364                 continue;
40365             }
40366             reg.showPanel(reg.getPanel(0));
40367         }
40368         
40369         
40370         
40371         
40372     },
40373     
40374     /**
40375      * Returns the nested BorderLayout for this panel
40376      * @return {Roo.BorderLayout} 
40377      */
40378     getLayout : function(){
40379         return this.layout;
40380     },
40381     
40382      /**
40383      * Adds a xtype elements to the layout of the nested panel
40384      * <pre><code>
40385
40386 panel.addxtype({
40387        xtype : 'ContentPanel',
40388        region: 'west',
40389        items: [ .... ]
40390    }
40391 );
40392
40393 panel.addxtype({
40394         xtype : 'NestedLayoutPanel',
40395         region: 'west',
40396         layout: {
40397            center: { },
40398            west: { }   
40399         },
40400         items : [ ... list of content panels or nested layout panels.. ]
40401    }
40402 );
40403 </code></pre>
40404      * @param {Object} cfg Xtype definition of item to add.
40405      */
40406     addxtype : function(cfg) {
40407         return this.layout.addxtype(cfg);
40408     
40409     }
40410 });/*
40411  * Based on:
40412  * Ext JS Library 1.1.1
40413  * Copyright(c) 2006-2007, Ext JS, LLC.
40414  *
40415  * Originally Released Under LGPL - original licence link has changed is not relivant.
40416  *
40417  * Fork - LGPL
40418  * <script type="text/javascript">
40419  */
40420 /**
40421  * @class Roo.TabPanel
40422  * @extends Roo.util.Observable
40423  * A lightweight tab container.
40424  * <br><br>
40425  * Usage:
40426  * <pre><code>
40427 // basic tabs 1, built from existing content
40428 var tabs = new Roo.TabPanel("tabs1");
40429 tabs.addTab("script", "View Script");
40430 tabs.addTab("markup", "View Markup");
40431 tabs.activate("script");
40432
40433 // more advanced tabs, built from javascript
40434 var jtabs = new Roo.TabPanel("jtabs");
40435 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40436
40437 // set up the UpdateManager
40438 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40439 var updater = tab2.getUpdateManager();
40440 updater.setDefaultUrl("ajax1.htm");
40441 tab2.on('activate', updater.refresh, updater, true);
40442
40443 // Use setUrl for Ajax loading
40444 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40445 tab3.setUrl("ajax2.htm", null, true);
40446
40447 // Disabled tab
40448 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40449 tab4.disable();
40450
40451 jtabs.activate("jtabs-1");
40452  * </code></pre>
40453  * @constructor
40454  * Create a new TabPanel.
40455  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40456  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40457  */
40458 Roo.bootstrap.panel.Tabs = function(config){
40459     /**
40460     * The container element for this TabPanel.
40461     * @type Roo.Element
40462     */
40463     this.el = Roo.get(config.el);
40464     delete config.el;
40465     if(config){
40466         if(typeof config == "boolean"){
40467             this.tabPosition = config ? "bottom" : "top";
40468         }else{
40469             Roo.apply(this, config);
40470         }
40471     }
40472     
40473     if(this.tabPosition == "bottom"){
40474         // if tabs are at the bottom = create the body first.
40475         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40476         this.el.addClass("roo-tabs-bottom");
40477     }
40478     // next create the tabs holders
40479     
40480     if (this.tabPosition == "west"){
40481         
40482         var reg = this.region; // fake it..
40483         while (reg) {
40484             if (!reg.mgr.parent) {
40485                 break;
40486             }
40487             reg = reg.mgr.parent.region;
40488         }
40489         Roo.log("got nest?");
40490         Roo.log(reg);
40491         if (reg.mgr.getRegion('west')) {
40492             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40493             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40494             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40495             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40496             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40497         
40498             
40499         }
40500         
40501         
40502     } else {
40503      
40504         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40505         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40506         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40507         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40508     }
40509     
40510     
40511     if(Roo.isIE){
40512         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40513     }
40514     
40515     // finally - if tabs are at the top, then create the body last..
40516     if(this.tabPosition != "bottom"){
40517         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40518          * @type Roo.Element
40519          */
40520         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40521         this.el.addClass("roo-tabs-top");
40522     }
40523     this.items = [];
40524
40525     this.bodyEl.setStyle("position", "relative");
40526
40527     this.active = null;
40528     this.activateDelegate = this.activate.createDelegate(this);
40529
40530     this.addEvents({
40531         /**
40532          * @event tabchange
40533          * Fires when the active tab changes
40534          * @param {Roo.TabPanel} this
40535          * @param {Roo.TabPanelItem} activePanel The new active tab
40536          */
40537         "tabchange": true,
40538         /**
40539          * @event beforetabchange
40540          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40541          * @param {Roo.TabPanel} this
40542          * @param {Object} e Set cancel to true on this object to cancel the tab change
40543          * @param {Roo.TabPanelItem} tab The tab being changed to
40544          */
40545         "beforetabchange" : true
40546     });
40547
40548     Roo.EventManager.onWindowResize(this.onResize, this);
40549     this.cpad = this.el.getPadding("lr");
40550     this.hiddenCount = 0;
40551
40552
40553     // toolbar on the tabbar support...
40554     if (this.toolbar) {
40555         alert("no toolbar support yet");
40556         this.toolbar  = false;
40557         /*
40558         var tcfg = this.toolbar;
40559         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40560         this.toolbar = new Roo.Toolbar(tcfg);
40561         if (Roo.isSafari) {
40562             var tbl = tcfg.container.child('table', true);
40563             tbl.setAttribute('width', '100%');
40564         }
40565         */
40566         
40567     }
40568    
40569
40570
40571     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40572 };
40573
40574 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40575     /*
40576      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40577      */
40578     tabPosition : "top",
40579     /*
40580      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40581      */
40582     currentTabWidth : 0,
40583     /*
40584      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40585      */
40586     minTabWidth : 40,
40587     /*
40588      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40589      */
40590     maxTabWidth : 250,
40591     /*
40592      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40593      */
40594     preferredTabWidth : 175,
40595     /*
40596      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40597      */
40598     resizeTabs : false,
40599     /*
40600      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40601      */
40602     monitorResize : true,
40603     /*
40604      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40605      */
40606     toolbar : false,  // set by caller..
40607     
40608     region : false, /// set by caller
40609     
40610     disableTooltips : true, // not used yet...
40611
40612     /**
40613      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40614      * @param {String} id The id of the div to use <b>or create</b>
40615      * @param {String} text The text for the tab
40616      * @param {String} content (optional) Content to put in the TabPanelItem body
40617      * @param {Boolean} closable (optional) True to create a close icon on the tab
40618      * @return {Roo.TabPanelItem} The created TabPanelItem
40619      */
40620     addTab : function(id, text, content, closable, tpl)
40621     {
40622         var item = new Roo.bootstrap.panel.TabItem({
40623             panel: this,
40624             id : id,
40625             text : text,
40626             closable : closable,
40627             tpl : tpl
40628         });
40629         this.addTabItem(item);
40630         if(content){
40631             item.setContent(content);
40632         }
40633         return item;
40634     },
40635
40636     /**
40637      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40638      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40639      * @return {Roo.TabPanelItem}
40640      */
40641     getTab : function(id){
40642         return this.items[id];
40643     },
40644
40645     /**
40646      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40647      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40648      */
40649     hideTab : function(id){
40650         var t = this.items[id];
40651         if(!t.isHidden()){
40652            t.setHidden(true);
40653            this.hiddenCount++;
40654            this.autoSizeTabs();
40655         }
40656     },
40657
40658     /**
40659      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40660      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40661      */
40662     unhideTab : function(id){
40663         var t = this.items[id];
40664         if(t.isHidden()){
40665            t.setHidden(false);
40666            this.hiddenCount--;
40667            this.autoSizeTabs();
40668         }
40669     },
40670
40671     /**
40672      * Adds an existing {@link Roo.TabPanelItem}.
40673      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40674      */
40675     addTabItem : function(item)
40676     {
40677         this.items[item.id] = item;
40678         this.items.push(item);
40679         this.autoSizeTabs();
40680       //  if(this.resizeTabs){
40681     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40682   //         this.autoSizeTabs();
40683 //        }else{
40684 //            item.autoSize();
40685        // }
40686     },
40687
40688     /**
40689      * Removes a {@link Roo.TabPanelItem}.
40690      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40691      */
40692     removeTab : function(id){
40693         var items = this.items;
40694         var tab = items[id];
40695         if(!tab) { return; }
40696         var index = items.indexOf(tab);
40697         if(this.active == tab && items.length > 1){
40698             var newTab = this.getNextAvailable(index);
40699             if(newTab) {
40700                 newTab.activate();
40701             }
40702         }
40703         this.stripEl.dom.removeChild(tab.pnode.dom);
40704         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40705             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40706         }
40707         items.splice(index, 1);
40708         delete this.items[tab.id];
40709         tab.fireEvent("close", tab);
40710         tab.purgeListeners();
40711         this.autoSizeTabs();
40712     },
40713
40714     getNextAvailable : function(start){
40715         var items = this.items;
40716         var index = start;
40717         // look for a next tab that will slide over to
40718         // replace the one being removed
40719         while(index < items.length){
40720             var item = items[++index];
40721             if(item && !item.isHidden()){
40722                 return item;
40723             }
40724         }
40725         // if one isn't found select the previous tab (on the left)
40726         index = start;
40727         while(index >= 0){
40728             var item = items[--index];
40729             if(item && !item.isHidden()){
40730                 return item;
40731             }
40732         }
40733         return null;
40734     },
40735
40736     /**
40737      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40738      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40739      */
40740     disableTab : function(id){
40741         var tab = this.items[id];
40742         if(tab && this.active != tab){
40743             tab.disable();
40744         }
40745     },
40746
40747     /**
40748      * Enables a {@link Roo.TabPanelItem} that is disabled.
40749      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40750      */
40751     enableTab : function(id){
40752         var tab = this.items[id];
40753         tab.enable();
40754     },
40755
40756     /**
40757      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40758      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40759      * @return {Roo.TabPanelItem} The TabPanelItem.
40760      */
40761     activate : function(id)
40762     {
40763         //Roo.log('activite:'  + id);
40764         
40765         var tab = this.items[id];
40766         if(!tab){
40767             return null;
40768         }
40769         if(tab == this.active || tab.disabled){
40770             return tab;
40771         }
40772         var e = {};
40773         this.fireEvent("beforetabchange", this, e, tab);
40774         if(e.cancel !== true && !tab.disabled){
40775             if(this.active){
40776                 this.active.hide();
40777             }
40778             this.active = this.items[id];
40779             this.active.show();
40780             this.fireEvent("tabchange", this, this.active);
40781         }
40782         return tab;
40783     },
40784
40785     /**
40786      * Gets the active {@link Roo.TabPanelItem}.
40787      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40788      */
40789     getActiveTab : function(){
40790         return this.active;
40791     },
40792
40793     /**
40794      * Updates the tab body element to fit the height of the container element
40795      * for overflow scrolling
40796      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40797      */
40798     syncHeight : function(targetHeight){
40799         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40800         var bm = this.bodyEl.getMargins();
40801         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40802         this.bodyEl.setHeight(newHeight);
40803         return newHeight;
40804     },
40805
40806     onResize : function(){
40807         if(this.monitorResize){
40808             this.autoSizeTabs();
40809         }
40810     },
40811
40812     /**
40813      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40814      */
40815     beginUpdate : function(){
40816         this.updating = true;
40817     },
40818
40819     /**
40820      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40821      */
40822     endUpdate : function(){
40823         this.updating = false;
40824         this.autoSizeTabs();
40825     },
40826
40827     /**
40828      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40829      */
40830     autoSizeTabs : function()
40831     {
40832         var count = this.items.length;
40833         var vcount = count - this.hiddenCount;
40834         
40835         if (vcount < 2) {
40836             this.stripEl.hide();
40837         } else {
40838             this.stripEl.show();
40839         }
40840         
40841         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40842             return;
40843         }
40844         
40845         
40846         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40847         var availWidth = Math.floor(w / vcount);
40848         var b = this.stripBody;
40849         if(b.getWidth() > w){
40850             var tabs = this.items;
40851             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40852             if(availWidth < this.minTabWidth){
40853                 /*if(!this.sleft){    // incomplete scrolling code
40854                     this.createScrollButtons();
40855                 }
40856                 this.showScroll();
40857                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40858             }
40859         }else{
40860             if(this.currentTabWidth < this.preferredTabWidth){
40861                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40862             }
40863         }
40864     },
40865
40866     /**
40867      * Returns the number of tabs in this TabPanel.
40868      * @return {Number}
40869      */
40870      getCount : function(){
40871          return this.items.length;
40872      },
40873
40874     /**
40875      * Resizes all the tabs to the passed width
40876      * @param {Number} The new width
40877      */
40878     setTabWidth : function(width){
40879         this.currentTabWidth = width;
40880         for(var i = 0, len = this.items.length; i < len; i++) {
40881                 if(!this.items[i].isHidden()) {
40882                 this.items[i].setWidth(width);
40883             }
40884         }
40885     },
40886
40887     /**
40888      * Destroys this TabPanel
40889      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40890      */
40891     destroy : function(removeEl){
40892         Roo.EventManager.removeResizeListener(this.onResize, this);
40893         for(var i = 0, len = this.items.length; i < len; i++){
40894             this.items[i].purgeListeners();
40895         }
40896         if(removeEl === true){
40897             this.el.update("");
40898             this.el.remove();
40899         }
40900     },
40901     
40902     createStrip : function(container)
40903     {
40904         var strip = document.createElement("nav");
40905         strip.className = Roo.bootstrap.version == 4 ?
40906             "navbar-light bg-light" : 
40907             "navbar navbar-default"; //"x-tabs-wrap";
40908         container.appendChild(strip);
40909         return strip;
40910     },
40911     
40912     createStripList : function(strip)
40913     {
40914         // div wrapper for retard IE
40915         // returns the "tr" element.
40916         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40917         //'<div class="x-tabs-strip-wrap">'+
40918           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40919           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40920         return strip.firstChild; //.firstChild.firstChild.firstChild;
40921     },
40922     createBody : function(container)
40923     {
40924         var body = document.createElement("div");
40925         Roo.id(body, "tab-body");
40926         //Roo.fly(body).addClass("x-tabs-body");
40927         Roo.fly(body).addClass("tab-content");
40928         container.appendChild(body);
40929         return body;
40930     },
40931     createItemBody :function(bodyEl, id){
40932         var body = Roo.getDom(id);
40933         if(!body){
40934             body = document.createElement("div");
40935             body.id = id;
40936         }
40937         //Roo.fly(body).addClass("x-tabs-item-body");
40938         Roo.fly(body).addClass("tab-pane");
40939          bodyEl.insertBefore(body, bodyEl.firstChild);
40940         return body;
40941     },
40942     /** @private */
40943     createStripElements :  function(stripEl, text, closable, tpl)
40944     {
40945         var td = document.createElement("li"); // was td..
40946         td.className = 'nav-item';
40947         
40948         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40949         
40950         
40951         stripEl.appendChild(td);
40952         /*if(closable){
40953             td.className = "x-tabs-closable";
40954             if(!this.closeTpl){
40955                 this.closeTpl = new Roo.Template(
40956                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40957                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40958                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40959                 );
40960             }
40961             var el = this.closeTpl.overwrite(td, {"text": text});
40962             var close = el.getElementsByTagName("div")[0];
40963             var inner = el.getElementsByTagName("em")[0];
40964             return {"el": el, "close": close, "inner": inner};
40965         } else {
40966         */
40967         // not sure what this is..
40968 //            if(!this.tabTpl){
40969                 //this.tabTpl = new Roo.Template(
40970                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40971                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40972                 //);
40973 //                this.tabTpl = new Roo.Template(
40974 //                   '<a href="#">' +
40975 //                   '<span unselectable="on"' +
40976 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40977 //                            ' >{text}</span></a>'
40978 //                );
40979 //                
40980 //            }
40981
40982
40983             var template = tpl || this.tabTpl || false;
40984             
40985             if(!template){
40986                 template =  new Roo.Template(
40987                         Roo.bootstrap.version == 4 ? 
40988                             (
40989                                 '<a class="nav-link" href="#" unselectable="on"' +
40990                                      (this.disableTooltips ? '' : ' title="{text}"') +
40991                                      ' >{text}</a>'
40992                             ) : (
40993                                 '<a class="nav-link" href="#">' +
40994                                 '<span unselectable="on"' +
40995                                          (this.disableTooltips ? '' : ' title="{text}"') +
40996                                     ' >{text}</span></a>'
40997                             )
40998                 );
40999             }
41000             
41001             switch (typeof(template)) {
41002                 case 'object' :
41003                     break;
41004                 case 'string' :
41005                     template = new Roo.Template(template);
41006                     break;
41007                 default :
41008                     break;
41009             }
41010             
41011             var el = template.overwrite(td, {"text": text});
41012             
41013             var inner = el.getElementsByTagName("span")[0];
41014             
41015             return {"el": el, "inner": inner};
41016             
41017     }
41018         
41019     
41020 });
41021
41022 /**
41023  * @class Roo.TabPanelItem
41024  * @extends Roo.util.Observable
41025  * Represents an individual item (tab plus body) in a TabPanel.
41026  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41027  * @param {String} id The id of this TabPanelItem
41028  * @param {String} text The text for the tab of this TabPanelItem
41029  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41030  */
41031 Roo.bootstrap.panel.TabItem = function(config){
41032     /**
41033      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41034      * @type Roo.TabPanel
41035      */
41036     this.tabPanel = config.panel;
41037     /**
41038      * The id for this TabPanelItem
41039      * @type String
41040      */
41041     this.id = config.id;
41042     /** @private */
41043     this.disabled = false;
41044     /** @private */
41045     this.text = config.text;
41046     /** @private */
41047     this.loaded = false;
41048     this.closable = config.closable;
41049
41050     /**
41051      * The body element for this TabPanelItem.
41052      * @type Roo.Element
41053      */
41054     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41055     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41056     this.bodyEl.setStyle("display", "block");
41057     this.bodyEl.setStyle("zoom", "1");
41058     //this.hideAction();
41059
41060     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41061     /** @private */
41062     this.el = Roo.get(els.el);
41063     this.inner = Roo.get(els.inner, true);
41064      this.textEl = Roo.bootstrap.version == 4 ?
41065         this.el : Roo.get(this.el.dom.firstChild, true);
41066
41067     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41068     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41069
41070     
41071 //    this.el.on("mousedown", this.onTabMouseDown, this);
41072     this.el.on("click", this.onTabClick, this);
41073     /** @private */
41074     if(config.closable){
41075         var c = Roo.get(els.close, true);
41076         c.dom.title = this.closeText;
41077         c.addClassOnOver("close-over");
41078         c.on("click", this.closeClick, this);
41079      }
41080
41081     this.addEvents({
41082          /**
41083          * @event activate
41084          * Fires when this tab becomes the active tab.
41085          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41086          * @param {Roo.TabPanelItem} this
41087          */
41088         "activate": true,
41089         /**
41090          * @event beforeclose
41091          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41092          * @param {Roo.TabPanelItem} this
41093          * @param {Object} e Set cancel to true on this object to cancel the close.
41094          */
41095         "beforeclose": true,
41096         /**
41097          * @event close
41098          * Fires when this tab is closed.
41099          * @param {Roo.TabPanelItem} this
41100          */
41101          "close": true,
41102         /**
41103          * @event deactivate
41104          * Fires when this tab is no longer the active tab.
41105          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41106          * @param {Roo.TabPanelItem} this
41107          */
41108          "deactivate" : true
41109     });
41110     this.hidden = false;
41111
41112     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41113 };
41114
41115 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41116            {
41117     purgeListeners : function(){
41118        Roo.util.Observable.prototype.purgeListeners.call(this);
41119        this.el.removeAllListeners();
41120     },
41121     /**
41122      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41123      */
41124     show : function(){
41125         this.status_node.addClass("active");
41126         this.showAction();
41127         if(Roo.isOpera){
41128             this.tabPanel.stripWrap.repaint();
41129         }
41130         this.fireEvent("activate", this.tabPanel, this);
41131     },
41132
41133     /**
41134      * Returns true if this tab is the active tab.
41135      * @return {Boolean}
41136      */
41137     isActive : function(){
41138         return this.tabPanel.getActiveTab() == this;
41139     },
41140
41141     /**
41142      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41143      */
41144     hide : function(){
41145         this.status_node.removeClass("active");
41146         this.hideAction();
41147         this.fireEvent("deactivate", this.tabPanel, this);
41148     },
41149
41150     hideAction : function(){
41151         this.bodyEl.hide();
41152         this.bodyEl.setStyle("position", "absolute");
41153         this.bodyEl.setLeft("-20000px");
41154         this.bodyEl.setTop("-20000px");
41155     },
41156
41157     showAction : function(){
41158         this.bodyEl.setStyle("position", "relative");
41159         this.bodyEl.setTop("");
41160         this.bodyEl.setLeft("");
41161         this.bodyEl.show();
41162     },
41163
41164     /**
41165      * Set the tooltip for the tab.
41166      * @param {String} tooltip The tab's tooltip
41167      */
41168     setTooltip : function(text){
41169         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41170             this.textEl.dom.qtip = text;
41171             this.textEl.dom.removeAttribute('title');
41172         }else{
41173             this.textEl.dom.title = text;
41174         }
41175     },
41176
41177     onTabClick : function(e){
41178         e.preventDefault();
41179         this.tabPanel.activate(this.id);
41180     },
41181
41182     onTabMouseDown : function(e){
41183         e.preventDefault();
41184         this.tabPanel.activate(this.id);
41185     },
41186 /*
41187     getWidth : function(){
41188         return this.inner.getWidth();
41189     },
41190
41191     setWidth : function(width){
41192         var iwidth = width - this.linode.getPadding("lr");
41193         this.inner.setWidth(iwidth);
41194         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41195         this.linode.setWidth(width);
41196     },
41197 */
41198     /**
41199      * Show or hide the tab
41200      * @param {Boolean} hidden True to hide or false to show.
41201      */
41202     setHidden : function(hidden){
41203         this.hidden = hidden;
41204         this.linode.setStyle("display", hidden ? "none" : "");
41205     },
41206
41207     /**
41208      * Returns true if this tab is "hidden"
41209      * @return {Boolean}
41210      */
41211     isHidden : function(){
41212         return this.hidden;
41213     },
41214
41215     /**
41216      * Returns the text for this tab
41217      * @return {String}
41218      */
41219     getText : function(){
41220         return this.text;
41221     },
41222     /*
41223     autoSize : function(){
41224         //this.el.beginMeasure();
41225         this.textEl.setWidth(1);
41226         /*
41227          *  #2804 [new] Tabs in Roojs
41228          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41229          */
41230         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41231         //this.el.endMeasure();
41232     //},
41233
41234     /**
41235      * Sets the text for the tab (Note: this also sets the tooltip text)
41236      * @param {String} text The tab's text and tooltip
41237      */
41238     setText : function(text){
41239         this.text = text;
41240         this.textEl.update(text);
41241         this.setTooltip(text);
41242         //if(!this.tabPanel.resizeTabs){
41243         //    this.autoSize();
41244         //}
41245     },
41246     /**
41247      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41248      */
41249     activate : function(){
41250         this.tabPanel.activate(this.id);
41251     },
41252
41253     /**
41254      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41255      */
41256     disable : function(){
41257         if(this.tabPanel.active != this){
41258             this.disabled = true;
41259             this.status_node.addClass("disabled");
41260         }
41261     },
41262
41263     /**
41264      * Enables this TabPanelItem if it was previously disabled.
41265      */
41266     enable : function(){
41267         this.disabled = false;
41268         this.status_node.removeClass("disabled");
41269     },
41270
41271     /**
41272      * Sets the content for this TabPanelItem.
41273      * @param {String} content The content
41274      * @param {Boolean} loadScripts true to look for and load scripts
41275      */
41276     setContent : function(content, loadScripts){
41277         this.bodyEl.update(content, loadScripts);
41278     },
41279
41280     /**
41281      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41282      * @return {Roo.UpdateManager} The UpdateManager
41283      */
41284     getUpdateManager : function(){
41285         return this.bodyEl.getUpdateManager();
41286     },
41287
41288     /**
41289      * Set a URL to be used to load the content for this TabPanelItem.
41290      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41291      * @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)
41292      * @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)
41293      * @return {Roo.UpdateManager} The UpdateManager
41294      */
41295     setUrl : function(url, params, loadOnce){
41296         if(this.refreshDelegate){
41297             this.un('activate', this.refreshDelegate);
41298         }
41299         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41300         this.on("activate", this.refreshDelegate);
41301         return this.bodyEl.getUpdateManager();
41302     },
41303
41304     /** @private */
41305     _handleRefresh : function(url, params, loadOnce){
41306         if(!loadOnce || !this.loaded){
41307             var updater = this.bodyEl.getUpdateManager();
41308             updater.update(url, params, this._setLoaded.createDelegate(this));
41309         }
41310     },
41311
41312     /**
41313      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41314      *   Will fail silently if the setUrl method has not been called.
41315      *   This does not activate the panel, just updates its content.
41316      */
41317     refresh : function(){
41318         if(this.refreshDelegate){
41319            this.loaded = false;
41320            this.refreshDelegate();
41321         }
41322     },
41323
41324     /** @private */
41325     _setLoaded : function(){
41326         this.loaded = true;
41327     },
41328
41329     /** @private */
41330     closeClick : function(e){
41331         var o = {};
41332         e.stopEvent();
41333         this.fireEvent("beforeclose", this, o);
41334         if(o.cancel !== true){
41335             this.tabPanel.removeTab(this.id);
41336         }
41337     },
41338     /**
41339      * The text displayed in the tooltip for the close icon.
41340      * @type String
41341      */
41342     closeText : "Close this tab"
41343 });
41344 /**
41345 *    This script refer to:
41346 *    Title: International Telephone Input
41347 *    Author: Jack O'Connor
41348 *    Code version:  v12.1.12
41349 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41350 **/
41351
41352 Roo.bootstrap.PhoneInputData = function() {
41353     var d = [
41354       [
41355         "Afghanistan (‫افغانستان‬‎)",
41356         "af",
41357         "93"
41358       ],
41359       [
41360         "Albania (Shqipëri)",
41361         "al",
41362         "355"
41363       ],
41364       [
41365         "Algeria (‫الجزائر‬‎)",
41366         "dz",
41367         "213"
41368       ],
41369       [
41370         "American Samoa",
41371         "as",
41372         "1684"
41373       ],
41374       [
41375         "Andorra",
41376         "ad",
41377         "376"
41378       ],
41379       [
41380         "Angola",
41381         "ao",
41382         "244"
41383       ],
41384       [
41385         "Anguilla",
41386         "ai",
41387         "1264"
41388       ],
41389       [
41390         "Antigua and Barbuda",
41391         "ag",
41392         "1268"
41393       ],
41394       [
41395         "Argentina",
41396         "ar",
41397         "54"
41398       ],
41399       [
41400         "Armenia (Հայաստան)",
41401         "am",
41402         "374"
41403       ],
41404       [
41405         "Aruba",
41406         "aw",
41407         "297"
41408       ],
41409       [
41410         "Australia",
41411         "au",
41412         "61",
41413         0
41414       ],
41415       [
41416         "Austria (Österreich)",
41417         "at",
41418         "43"
41419       ],
41420       [
41421         "Azerbaijan (Azərbaycan)",
41422         "az",
41423         "994"
41424       ],
41425       [
41426         "Bahamas",
41427         "bs",
41428         "1242"
41429       ],
41430       [
41431         "Bahrain (‫البحرين‬‎)",
41432         "bh",
41433         "973"
41434       ],
41435       [
41436         "Bangladesh (বাংলাদেশ)",
41437         "bd",
41438         "880"
41439       ],
41440       [
41441         "Barbados",
41442         "bb",
41443         "1246"
41444       ],
41445       [
41446         "Belarus (Беларусь)",
41447         "by",
41448         "375"
41449       ],
41450       [
41451         "Belgium (België)",
41452         "be",
41453         "32"
41454       ],
41455       [
41456         "Belize",
41457         "bz",
41458         "501"
41459       ],
41460       [
41461         "Benin (Bénin)",
41462         "bj",
41463         "229"
41464       ],
41465       [
41466         "Bermuda",
41467         "bm",
41468         "1441"
41469       ],
41470       [
41471         "Bhutan (འབྲུག)",
41472         "bt",
41473         "975"
41474       ],
41475       [
41476         "Bolivia",
41477         "bo",
41478         "591"
41479       ],
41480       [
41481         "Bosnia and Herzegovina (Босна и Херцеговина)",
41482         "ba",
41483         "387"
41484       ],
41485       [
41486         "Botswana",
41487         "bw",
41488         "267"
41489       ],
41490       [
41491         "Brazil (Brasil)",
41492         "br",
41493         "55"
41494       ],
41495       [
41496         "British Indian Ocean Territory",
41497         "io",
41498         "246"
41499       ],
41500       [
41501         "British Virgin Islands",
41502         "vg",
41503         "1284"
41504       ],
41505       [
41506         "Brunei",
41507         "bn",
41508         "673"
41509       ],
41510       [
41511         "Bulgaria (България)",
41512         "bg",
41513         "359"
41514       ],
41515       [
41516         "Burkina Faso",
41517         "bf",
41518         "226"
41519       ],
41520       [
41521         "Burundi (Uburundi)",
41522         "bi",
41523         "257"
41524       ],
41525       [
41526         "Cambodia (កម្ពុជា)",
41527         "kh",
41528         "855"
41529       ],
41530       [
41531         "Cameroon (Cameroun)",
41532         "cm",
41533         "237"
41534       ],
41535       [
41536         "Canada",
41537         "ca",
41538         "1",
41539         1,
41540         ["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"]
41541       ],
41542       [
41543         "Cape Verde (Kabu Verdi)",
41544         "cv",
41545         "238"
41546       ],
41547       [
41548         "Caribbean Netherlands",
41549         "bq",
41550         "599",
41551         1
41552       ],
41553       [
41554         "Cayman Islands",
41555         "ky",
41556         "1345"
41557       ],
41558       [
41559         "Central African Republic (République centrafricaine)",
41560         "cf",
41561         "236"
41562       ],
41563       [
41564         "Chad (Tchad)",
41565         "td",
41566         "235"
41567       ],
41568       [
41569         "Chile",
41570         "cl",
41571         "56"
41572       ],
41573       [
41574         "China (中国)",
41575         "cn",
41576         "86"
41577       ],
41578       [
41579         "Christmas Island",
41580         "cx",
41581         "61",
41582         2
41583       ],
41584       [
41585         "Cocos (Keeling) Islands",
41586         "cc",
41587         "61",
41588         1
41589       ],
41590       [
41591         "Colombia",
41592         "co",
41593         "57"
41594       ],
41595       [
41596         "Comoros (‫جزر القمر‬‎)",
41597         "km",
41598         "269"
41599       ],
41600       [
41601         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41602         "cd",
41603         "243"
41604       ],
41605       [
41606         "Congo (Republic) (Congo-Brazzaville)",
41607         "cg",
41608         "242"
41609       ],
41610       [
41611         "Cook Islands",
41612         "ck",
41613         "682"
41614       ],
41615       [
41616         "Costa Rica",
41617         "cr",
41618         "506"
41619       ],
41620       [
41621         "Côte d’Ivoire",
41622         "ci",
41623         "225"
41624       ],
41625       [
41626         "Croatia (Hrvatska)",
41627         "hr",
41628         "385"
41629       ],
41630       [
41631         "Cuba",
41632         "cu",
41633         "53"
41634       ],
41635       [
41636         "Curaçao",
41637         "cw",
41638         "599",
41639         0
41640       ],
41641       [
41642         "Cyprus (Κύπρος)",
41643         "cy",
41644         "357"
41645       ],
41646       [
41647         "Czech Republic (Česká republika)",
41648         "cz",
41649         "420"
41650       ],
41651       [
41652         "Denmark (Danmark)",
41653         "dk",
41654         "45"
41655       ],
41656       [
41657         "Djibouti",
41658         "dj",
41659         "253"
41660       ],
41661       [
41662         "Dominica",
41663         "dm",
41664         "1767"
41665       ],
41666       [
41667         "Dominican Republic (República Dominicana)",
41668         "do",
41669         "1",
41670         2,
41671         ["809", "829", "849"]
41672       ],
41673       [
41674         "Ecuador",
41675         "ec",
41676         "593"
41677       ],
41678       [
41679         "Egypt (‫مصر‬‎)",
41680         "eg",
41681         "20"
41682       ],
41683       [
41684         "El Salvador",
41685         "sv",
41686         "503"
41687       ],
41688       [
41689         "Equatorial Guinea (Guinea Ecuatorial)",
41690         "gq",
41691         "240"
41692       ],
41693       [
41694         "Eritrea",
41695         "er",
41696         "291"
41697       ],
41698       [
41699         "Estonia (Eesti)",
41700         "ee",
41701         "372"
41702       ],
41703       [
41704         "Ethiopia",
41705         "et",
41706         "251"
41707       ],
41708       [
41709         "Falkland Islands (Islas Malvinas)",
41710         "fk",
41711         "500"
41712       ],
41713       [
41714         "Faroe Islands (Føroyar)",
41715         "fo",
41716         "298"
41717       ],
41718       [
41719         "Fiji",
41720         "fj",
41721         "679"
41722       ],
41723       [
41724         "Finland (Suomi)",
41725         "fi",
41726         "358",
41727         0
41728       ],
41729       [
41730         "France",
41731         "fr",
41732         "33"
41733       ],
41734       [
41735         "French Guiana (Guyane française)",
41736         "gf",
41737         "594"
41738       ],
41739       [
41740         "French Polynesia (Polynésie française)",
41741         "pf",
41742         "689"
41743       ],
41744       [
41745         "Gabon",
41746         "ga",
41747         "241"
41748       ],
41749       [
41750         "Gambia",
41751         "gm",
41752         "220"
41753       ],
41754       [
41755         "Georgia (საქართველო)",
41756         "ge",
41757         "995"
41758       ],
41759       [
41760         "Germany (Deutschland)",
41761         "de",
41762         "49"
41763       ],
41764       [
41765         "Ghana (Gaana)",
41766         "gh",
41767         "233"
41768       ],
41769       [
41770         "Gibraltar",
41771         "gi",
41772         "350"
41773       ],
41774       [
41775         "Greece (Ελλάδα)",
41776         "gr",
41777         "30"
41778       ],
41779       [
41780         "Greenland (Kalaallit Nunaat)",
41781         "gl",
41782         "299"
41783       ],
41784       [
41785         "Grenada",
41786         "gd",
41787         "1473"
41788       ],
41789       [
41790         "Guadeloupe",
41791         "gp",
41792         "590",
41793         0
41794       ],
41795       [
41796         "Guam",
41797         "gu",
41798         "1671"
41799       ],
41800       [
41801         "Guatemala",
41802         "gt",
41803         "502"
41804       ],
41805       [
41806         "Guernsey",
41807         "gg",
41808         "44",
41809         1
41810       ],
41811       [
41812         "Guinea (Guinée)",
41813         "gn",
41814         "224"
41815       ],
41816       [
41817         "Guinea-Bissau (Guiné Bissau)",
41818         "gw",
41819         "245"
41820       ],
41821       [
41822         "Guyana",
41823         "gy",
41824         "592"
41825       ],
41826       [
41827         "Haiti",
41828         "ht",
41829         "509"
41830       ],
41831       [
41832         "Honduras",
41833         "hn",
41834         "504"
41835       ],
41836       [
41837         "Hong Kong (香港)",
41838         "hk",
41839         "852"
41840       ],
41841       [
41842         "Hungary (Magyarország)",
41843         "hu",
41844         "36"
41845       ],
41846       [
41847         "Iceland (Ísland)",
41848         "is",
41849         "354"
41850       ],
41851       [
41852         "India (भारत)",
41853         "in",
41854         "91"
41855       ],
41856       [
41857         "Indonesia",
41858         "id",
41859         "62"
41860       ],
41861       [
41862         "Iran (‫ایران‬‎)",
41863         "ir",
41864         "98"
41865       ],
41866       [
41867         "Iraq (‫العراق‬‎)",
41868         "iq",
41869         "964"
41870       ],
41871       [
41872         "Ireland",
41873         "ie",
41874         "353"
41875       ],
41876       [
41877         "Isle of Man",
41878         "im",
41879         "44",
41880         2
41881       ],
41882       [
41883         "Israel (‫ישראל‬‎)",
41884         "il",
41885         "972"
41886       ],
41887       [
41888         "Italy (Italia)",
41889         "it",
41890         "39",
41891         0
41892       ],
41893       [
41894         "Jamaica",
41895         "jm",
41896         "1876"
41897       ],
41898       [
41899         "Japan (日本)",
41900         "jp",
41901         "81"
41902       ],
41903       [
41904         "Jersey",
41905         "je",
41906         "44",
41907         3
41908       ],
41909       [
41910         "Jordan (‫الأردن‬‎)",
41911         "jo",
41912         "962"
41913       ],
41914       [
41915         "Kazakhstan (Казахстан)",
41916         "kz",
41917         "7",
41918         1
41919       ],
41920       [
41921         "Kenya",
41922         "ke",
41923         "254"
41924       ],
41925       [
41926         "Kiribati",
41927         "ki",
41928         "686"
41929       ],
41930       [
41931         "Kosovo",
41932         "xk",
41933         "383"
41934       ],
41935       [
41936         "Kuwait (‫الكويت‬‎)",
41937         "kw",
41938         "965"
41939       ],
41940       [
41941         "Kyrgyzstan (Кыргызстан)",
41942         "kg",
41943         "996"
41944       ],
41945       [
41946         "Laos (ລາວ)",
41947         "la",
41948         "856"
41949       ],
41950       [
41951         "Latvia (Latvija)",
41952         "lv",
41953         "371"
41954       ],
41955       [
41956         "Lebanon (‫لبنان‬‎)",
41957         "lb",
41958         "961"
41959       ],
41960       [
41961         "Lesotho",
41962         "ls",
41963         "266"
41964       ],
41965       [
41966         "Liberia",
41967         "lr",
41968         "231"
41969       ],
41970       [
41971         "Libya (‫ليبيا‬‎)",
41972         "ly",
41973         "218"
41974       ],
41975       [
41976         "Liechtenstein",
41977         "li",
41978         "423"
41979       ],
41980       [
41981         "Lithuania (Lietuva)",
41982         "lt",
41983         "370"
41984       ],
41985       [
41986         "Luxembourg",
41987         "lu",
41988         "352"
41989       ],
41990       [
41991         "Macau (澳門)",
41992         "mo",
41993         "853"
41994       ],
41995       [
41996         "Macedonia (FYROM) (Македонија)",
41997         "mk",
41998         "389"
41999       ],
42000       [
42001         "Madagascar (Madagasikara)",
42002         "mg",
42003         "261"
42004       ],
42005       [
42006         "Malawi",
42007         "mw",
42008         "265"
42009       ],
42010       [
42011         "Malaysia",
42012         "my",
42013         "60"
42014       ],
42015       [
42016         "Maldives",
42017         "mv",
42018         "960"
42019       ],
42020       [
42021         "Mali",
42022         "ml",
42023         "223"
42024       ],
42025       [
42026         "Malta",
42027         "mt",
42028         "356"
42029       ],
42030       [
42031         "Marshall Islands",
42032         "mh",
42033         "692"
42034       ],
42035       [
42036         "Martinique",
42037         "mq",
42038         "596"
42039       ],
42040       [
42041         "Mauritania (‫موريتانيا‬‎)",
42042         "mr",
42043         "222"
42044       ],
42045       [
42046         "Mauritius (Moris)",
42047         "mu",
42048         "230"
42049       ],
42050       [
42051         "Mayotte",
42052         "yt",
42053         "262",
42054         1
42055       ],
42056       [
42057         "Mexico (México)",
42058         "mx",
42059         "52"
42060       ],
42061       [
42062         "Micronesia",
42063         "fm",
42064         "691"
42065       ],
42066       [
42067         "Moldova (Republica Moldova)",
42068         "md",
42069         "373"
42070       ],
42071       [
42072         "Monaco",
42073         "mc",
42074         "377"
42075       ],
42076       [
42077         "Mongolia (Монгол)",
42078         "mn",
42079         "976"
42080       ],
42081       [
42082         "Montenegro (Crna Gora)",
42083         "me",
42084         "382"
42085       ],
42086       [
42087         "Montserrat",
42088         "ms",
42089         "1664"
42090       ],
42091       [
42092         "Morocco (‫المغرب‬‎)",
42093         "ma",
42094         "212",
42095         0
42096       ],
42097       [
42098         "Mozambique (Moçambique)",
42099         "mz",
42100         "258"
42101       ],
42102       [
42103         "Myanmar (Burma) (မြန်မာ)",
42104         "mm",
42105         "95"
42106       ],
42107       [
42108         "Namibia (Namibië)",
42109         "na",
42110         "264"
42111       ],
42112       [
42113         "Nauru",
42114         "nr",
42115         "674"
42116       ],
42117       [
42118         "Nepal (नेपाल)",
42119         "np",
42120         "977"
42121       ],
42122       [
42123         "Netherlands (Nederland)",
42124         "nl",
42125         "31"
42126       ],
42127       [
42128         "New Caledonia (Nouvelle-Calédonie)",
42129         "nc",
42130         "687"
42131       ],
42132       [
42133         "New Zealand",
42134         "nz",
42135         "64"
42136       ],
42137       [
42138         "Nicaragua",
42139         "ni",
42140         "505"
42141       ],
42142       [
42143         "Niger (Nijar)",
42144         "ne",
42145         "227"
42146       ],
42147       [
42148         "Nigeria",
42149         "ng",
42150         "234"
42151       ],
42152       [
42153         "Niue",
42154         "nu",
42155         "683"
42156       ],
42157       [
42158         "Norfolk Island",
42159         "nf",
42160         "672"
42161       ],
42162       [
42163         "North Korea (조선 민주주의 인민 공화국)",
42164         "kp",
42165         "850"
42166       ],
42167       [
42168         "Northern Mariana Islands",
42169         "mp",
42170         "1670"
42171       ],
42172       [
42173         "Norway (Norge)",
42174         "no",
42175         "47",
42176         0
42177       ],
42178       [
42179         "Oman (‫عُمان‬‎)",
42180         "om",
42181         "968"
42182       ],
42183       [
42184         "Pakistan (‫پاکستان‬‎)",
42185         "pk",
42186         "92"
42187       ],
42188       [
42189         "Palau",
42190         "pw",
42191         "680"
42192       ],
42193       [
42194         "Palestine (‫فلسطين‬‎)",
42195         "ps",
42196         "970"
42197       ],
42198       [
42199         "Panama (Panamá)",
42200         "pa",
42201         "507"
42202       ],
42203       [
42204         "Papua New Guinea",
42205         "pg",
42206         "675"
42207       ],
42208       [
42209         "Paraguay",
42210         "py",
42211         "595"
42212       ],
42213       [
42214         "Peru (Perú)",
42215         "pe",
42216         "51"
42217       ],
42218       [
42219         "Philippines",
42220         "ph",
42221         "63"
42222       ],
42223       [
42224         "Poland (Polska)",
42225         "pl",
42226         "48"
42227       ],
42228       [
42229         "Portugal",
42230         "pt",
42231         "351"
42232       ],
42233       [
42234         "Puerto Rico",
42235         "pr",
42236         "1",
42237         3,
42238         ["787", "939"]
42239       ],
42240       [
42241         "Qatar (‫قطر‬‎)",
42242         "qa",
42243         "974"
42244       ],
42245       [
42246         "Réunion (La Réunion)",
42247         "re",
42248         "262",
42249         0
42250       ],
42251       [
42252         "Romania (România)",
42253         "ro",
42254         "40"
42255       ],
42256       [
42257         "Russia (Россия)",
42258         "ru",
42259         "7",
42260         0
42261       ],
42262       [
42263         "Rwanda",
42264         "rw",
42265         "250"
42266       ],
42267       [
42268         "Saint Barthélemy",
42269         "bl",
42270         "590",
42271         1
42272       ],
42273       [
42274         "Saint Helena",
42275         "sh",
42276         "290"
42277       ],
42278       [
42279         "Saint Kitts and Nevis",
42280         "kn",
42281         "1869"
42282       ],
42283       [
42284         "Saint Lucia",
42285         "lc",
42286         "1758"
42287       ],
42288       [
42289         "Saint Martin (Saint-Martin (partie française))",
42290         "mf",
42291         "590",
42292         2
42293       ],
42294       [
42295         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42296         "pm",
42297         "508"
42298       ],
42299       [
42300         "Saint Vincent and the Grenadines",
42301         "vc",
42302         "1784"
42303       ],
42304       [
42305         "Samoa",
42306         "ws",
42307         "685"
42308       ],
42309       [
42310         "San Marino",
42311         "sm",
42312         "378"
42313       ],
42314       [
42315         "São Tomé and Príncipe (São Tomé e Príncipe)",
42316         "st",
42317         "239"
42318       ],
42319       [
42320         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42321         "sa",
42322         "966"
42323       ],
42324       [
42325         "Senegal (Sénégal)",
42326         "sn",
42327         "221"
42328       ],
42329       [
42330         "Serbia (Србија)",
42331         "rs",
42332         "381"
42333       ],
42334       [
42335         "Seychelles",
42336         "sc",
42337         "248"
42338       ],
42339       [
42340         "Sierra Leone",
42341         "sl",
42342         "232"
42343       ],
42344       [
42345         "Singapore",
42346         "sg",
42347         "65"
42348       ],
42349       [
42350         "Sint Maarten",
42351         "sx",
42352         "1721"
42353       ],
42354       [
42355         "Slovakia (Slovensko)",
42356         "sk",
42357         "421"
42358       ],
42359       [
42360         "Slovenia (Slovenija)",
42361         "si",
42362         "386"
42363       ],
42364       [
42365         "Solomon Islands",
42366         "sb",
42367         "677"
42368       ],
42369       [
42370         "Somalia (Soomaaliya)",
42371         "so",
42372         "252"
42373       ],
42374       [
42375         "South Africa",
42376         "za",
42377         "27"
42378       ],
42379       [
42380         "South Korea (대한민국)",
42381         "kr",
42382         "82"
42383       ],
42384       [
42385         "South Sudan (‫جنوب السودان‬‎)",
42386         "ss",
42387         "211"
42388       ],
42389       [
42390         "Spain (España)",
42391         "es",
42392         "34"
42393       ],
42394       [
42395         "Sri Lanka (ශ්‍රී ලංකාව)",
42396         "lk",
42397         "94"
42398       ],
42399       [
42400         "Sudan (‫السودان‬‎)",
42401         "sd",
42402         "249"
42403       ],
42404       [
42405         "Suriname",
42406         "sr",
42407         "597"
42408       ],
42409       [
42410         "Svalbard and Jan Mayen",
42411         "sj",
42412         "47",
42413         1
42414       ],
42415       [
42416         "Swaziland",
42417         "sz",
42418         "268"
42419       ],
42420       [
42421         "Sweden (Sverige)",
42422         "se",
42423         "46"
42424       ],
42425       [
42426         "Switzerland (Schweiz)",
42427         "ch",
42428         "41"
42429       ],
42430       [
42431         "Syria (‫سوريا‬‎)",
42432         "sy",
42433         "963"
42434       ],
42435       [
42436         "Taiwan (台灣)",
42437         "tw",
42438         "886"
42439       ],
42440       [
42441         "Tajikistan",
42442         "tj",
42443         "992"
42444       ],
42445       [
42446         "Tanzania",
42447         "tz",
42448         "255"
42449       ],
42450       [
42451         "Thailand (ไทย)",
42452         "th",
42453         "66"
42454       ],
42455       [
42456         "Timor-Leste",
42457         "tl",
42458         "670"
42459       ],
42460       [
42461         "Togo",
42462         "tg",
42463         "228"
42464       ],
42465       [
42466         "Tokelau",
42467         "tk",
42468         "690"
42469       ],
42470       [
42471         "Tonga",
42472         "to",
42473         "676"
42474       ],
42475       [
42476         "Trinidad and Tobago",
42477         "tt",
42478         "1868"
42479       ],
42480       [
42481         "Tunisia (‫تونس‬‎)",
42482         "tn",
42483         "216"
42484       ],
42485       [
42486         "Turkey (Türkiye)",
42487         "tr",
42488         "90"
42489       ],
42490       [
42491         "Turkmenistan",
42492         "tm",
42493         "993"
42494       ],
42495       [
42496         "Turks and Caicos Islands",
42497         "tc",
42498         "1649"
42499       ],
42500       [
42501         "Tuvalu",
42502         "tv",
42503         "688"
42504       ],
42505       [
42506         "U.S. Virgin Islands",
42507         "vi",
42508         "1340"
42509       ],
42510       [
42511         "Uganda",
42512         "ug",
42513         "256"
42514       ],
42515       [
42516         "Ukraine (Україна)",
42517         "ua",
42518         "380"
42519       ],
42520       [
42521         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42522         "ae",
42523         "971"
42524       ],
42525       [
42526         "United Kingdom",
42527         "gb",
42528         "44",
42529         0
42530       ],
42531       [
42532         "United States",
42533         "us",
42534         "1",
42535         0
42536       ],
42537       [
42538         "Uruguay",
42539         "uy",
42540         "598"
42541       ],
42542       [
42543         "Uzbekistan (Oʻzbekiston)",
42544         "uz",
42545         "998"
42546       ],
42547       [
42548         "Vanuatu",
42549         "vu",
42550         "678"
42551       ],
42552       [
42553         "Vatican City (Città del Vaticano)",
42554         "va",
42555         "39",
42556         1
42557       ],
42558       [
42559         "Venezuela",
42560         "ve",
42561         "58"
42562       ],
42563       [
42564         "Vietnam (Việt Nam)",
42565         "vn",
42566         "84"
42567       ],
42568       [
42569         "Wallis and Futuna (Wallis-et-Futuna)",
42570         "wf",
42571         "681"
42572       ],
42573       [
42574         "Western Sahara (‫الصحراء الغربية‬‎)",
42575         "eh",
42576         "212",
42577         1
42578       ],
42579       [
42580         "Yemen (‫اليمن‬‎)",
42581         "ye",
42582         "967"
42583       ],
42584       [
42585         "Zambia",
42586         "zm",
42587         "260"
42588       ],
42589       [
42590         "Zimbabwe",
42591         "zw",
42592         "263"
42593       ],
42594       [
42595         "Åland Islands",
42596         "ax",
42597         "358",
42598         1
42599       ]
42600   ];
42601   
42602   return d;
42603 }/**
42604 *    This script refer to:
42605 *    Title: International Telephone Input
42606 *    Author: Jack O'Connor
42607 *    Code version:  v12.1.12
42608 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42609 **/
42610
42611 /**
42612  * @class Roo.bootstrap.PhoneInput
42613  * @extends Roo.bootstrap.TriggerField
42614  * An input with International dial-code selection
42615  
42616  * @cfg {String} defaultDialCode default '+852'
42617  * @cfg {Array} preferedCountries default []
42618   
42619  * @constructor
42620  * Create a new PhoneInput.
42621  * @param {Object} config Configuration options
42622  */
42623
42624 Roo.bootstrap.PhoneInput = function(config) {
42625     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42626 };
42627
42628 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42629         
42630         listWidth: undefined,
42631         
42632         selectedClass: 'active',
42633         
42634         invalidClass : "has-warning",
42635         
42636         validClass: 'has-success',
42637         
42638         allowed: '0123456789',
42639         
42640         max_length: 15,
42641         
42642         /**
42643          * @cfg {String} defaultDialCode The default dial code when initializing the input
42644          */
42645         defaultDialCode: '+852',
42646         
42647         /**
42648          * @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
42649          */
42650         preferedCountries: false,
42651         
42652         getAutoCreate : function()
42653         {
42654             var data = Roo.bootstrap.PhoneInputData();
42655             var align = this.labelAlign || this.parentLabelAlign();
42656             var id = Roo.id();
42657             
42658             this.allCountries = [];
42659             this.dialCodeMapping = [];
42660             
42661             for (var i = 0; i < data.length; i++) {
42662               var c = data[i];
42663               this.allCountries[i] = {
42664                 name: c[0],
42665                 iso2: c[1],
42666                 dialCode: c[2],
42667                 priority: c[3] || 0,
42668                 areaCodes: c[4] || null
42669               };
42670               this.dialCodeMapping[c[2]] = {
42671                   name: c[0],
42672                   iso2: c[1],
42673                   priority: c[3] || 0,
42674                   areaCodes: c[4] || null
42675               };
42676             }
42677             
42678             var cfg = {
42679                 cls: 'form-group',
42680                 cn: []
42681             };
42682             
42683             var input =  {
42684                 tag: 'input',
42685                 id : id,
42686                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42687                 maxlength: this.max_length,
42688                 cls : 'form-control tel-input',
42689                 autocomplete: 'new-password'
42690             };
42691             
42692             var hiddenInput = {
42693                 tag: 'input',
42694                 type: 'hidden',
42695                 cls: 'hidden-tel-input'
42696             };
42697             
42698             if (this.name) {
42699                 hiddenInput.name = this.name;
42700             }
42701             
42702             if (this.disabled) {
42703                 input.disabled = true;
42704             }
42705             
42706             var flag_container = {
42707                 tag: 'div',
42708                 cls: 'flag-box',
42709                 cn: [
42710                     {
42711                         tag: 'div',
42712                         cls: 'flag'
42713                     },
42714                     {
42715                         tag: 'div',
42716                         cls: 'caret'
42717                     }
42718                 ]
42719             };
42720             
42721             var box = {
42722                 tag: 'div',
42723                 cls: this.hasFeedback ? 'has-feedback' : '',
42724                 cn: [
42725                     hiddenInput,
42726                     input,
42727                     {
42728                         tag: 'input',
42729                         cls: 'dial-code-holder',
42730                         disabled: true
42731                     }
42732                 ]
42733             };
42734             
42735             var container = {
42736                 cls: 'roo-select2-container input-group',
42737                 cn: [
42738                     flag_container,
42739                     box
42740                 ]
42741             };
42742             
42743             if (this.fieldLabel.length) {
42744                 var indicator = {
42745                     tag: 'i',
42746                     tooltip: 'This field is required'
42747                 };
42748                 
42749                 var label = {
42750                     tag: 'label',
42751                     'for':  id,
42752                     cls: 'control-label',
42753                     cn: []
42754                 };
42755                 
42756                 var label_text = {
42757                     tag: 'span',
42758                     html: this.fieldLabel
42759                 };
42760                 
42761                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42762                 label.cn = [
42763                     indicator,
42764                     label_text
42765                 ];
42766                 
42767                 if(this.indicatorpos == 'right') {
42768                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42769                     label.cn = [
42770                         label_text,
42771                         indicator
42772                     ];
42773                 }
42774                 
42775                 if(align == 'left') {
42776                     container = {
42777                         tag: 'div',
42778                         cn: [
42779                             container
42780                         ]
42781                     };
42782                     
42783                     if(this.labelWidth > 12){
42784                         label.style = "width: " + this.labelWidth + 'px';
42785                     }
42786                     if(this.labelWidth < 13 && this.labelmd == 0){
42787                         this.labelmd = this.labelWidth;
42788                     }
42789                     if(this.labellg > 0){
42790                         label.cls += ' col-lg-' + this.labellg;
42791                         input.cls += ' col-lg-' + (12 - this.labellg);
42792                     }
42793                     if(this.labelmd > 0){
42794                         label.cls += ' col-md-' + this.labelmd;
42795                         container.cls += ' col-md-' + (12 - this.labelmd);
42796                     }
42797                     if(this.labelsm > 0){
42798                         label.cls += ' col-sm-' + this.labelsm;
42799                         container.cls += ' col-sm-' + (12 - this.labelsm);
42800                     }
42801                     if(this.labelxs > 0){
42802                         label.cls += ' col-xs-' + this.labelxs;
42803                         container.cls += ' col-xs-' + (12 - this.labelxs);
42804                     }
42805                 }
42806             }
42807             
42808             cfg.cn = [
42809                 label,
42810                 container
42811             ];
42812             
42813             var settings = this;
42814             
42815             ['xs','sm','md','lg'].map(function(size){
42816                 if (settings[size]) {
42817                     cfg.cls += ' col-' + size + '-' + settings[size];
42818                 }
42819             });
42820             
42821             this.store = new Roo.data.Store({
42822                 proxy : new Roo.data.MemoryProxy({}),
42823                 reader : new Roo.data.JsonReader({
42824                     fields : [
42825                         {
42826                             'name' : 'name',
42827                             'type' : 'string'
42828                         },
42829                         {
42830                             'name' : 'iso2',
42831                             'type' : 'string'
42832                         },
42833                         {
42834                             'name' : 'dialCode',
42835                             'type' : 'string'
42836                         },
42837                         {
42838                             'name' : 'priority',
42839                             'type' : 'string'
42840                         },
42841                         {
42842                             'name' : 'areaCodes',
42843                             'type' : 'string'
42844                         }
42845                     ]
42846                 })
42847             });
42848             
42849             if(!this.preferedCountries) {
42850                 this.preferedCountries = [
42851                     'hk',
42852                     'gb',
42853                     'us'
42854                 ];
42855             }
42856             
42857             var p = this.preferedCountries.reverse();
42858             
42859             if(p) {
42860                 for (var i = 0; i < p.length; i++) {
42861                     for (var j = 0; j < this.allCountries.length; j++) {
42862                         if(this.allCountries[j].iso2 == p[i]) {
42863                             var t = this.allCountries[j];
42864                             this.allCountries.splice(j,1);
42865                             this.allCountries.unshift(t);
42866                         }
42867                     } 
42868                 }
42869             }
42870             
42871             this.store.proxy.data = {
42872                 success: true,
42873                 data: this.allCountries
42874             };
42875             
42876             return cfg;
42877         },
42878         
42879         initEvents : function()
42880         {
42881             this.createList();
42882             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42883             
42884             this.indicator = this.indicatorEl();
42885             this.flag = this.flagEl();
42886             this.dialCodeHolder = this.dialCodeHolderEl();
42887             
42888             this.trigger = this.el.select('div.flag-box',true).first();
42889             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42890             
42891             var _this = this;
42892             
42893             (function(){
42894                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42895                 _this.list.setWidth(lw);
42896             }).defer(100);
42897             
42898             this.list.on('mouseover', this.onViewOver, this);
42899             this.list.on('mousemove', this.onViewMove, this);
42900             this.inputEl().on("keyup", this.onKeyUp, this);
42901             this.inputEl().on("keypress", this.onKeyPress, this);
42902             
42903             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42904
42905             this.view = new Roo.View(this.list, this.tpl, {
42906                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42907             });
42908             
42909             this.view.on('click', this.onViewClick, this);
42910             this.setValue(this.defaultDialCode);
42911         },
42912         
42913         onTriggerClick : function(e)
42914         {
42915             Roo.log('trigger click');
42916             if(this.disabled){
42917                 return;
42918             }
42919             
42920             if(this.isExpanded()){
42921                 this.collapse();
42922                 this.hasFocus = false;
42923             }else {
42924                 this.store.load({});
42925                 this.hasFocus = true;
42926                 this.expand();
42927             }
42928         },
42929         
42930         isExpanded : function()
42931         {
42932             return this.list.isVisible();
42933         },
42934         
42935         collapse : function()
42936         {
42937             if(!this.isExpanded()){
42938                 return;
42939             }
42940             this.list.hide();
42941             Roo.get(document).un('mousedown', this.collapseIf, this);
42942             Roo.get(document).un('mousewheel', this.collapseIf, this);
42943             this.fireEvent('collapse', this);
42944             this.validate();
42945         },
42946         
42947         expand : function()
42948         {
42949             Roo.log('expand');
42950
42951             if(this.isExpanded() || !this.hasFocus){
42952                 return;
42953             }
42954             
42955             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42956             this.list.setWidth(lw);
42957             
42958             this.list.show();
42959             this.restrictHeight();
42960             
42961             Roo.get(document).on('mousedown', this.collapseIf, this);
42962             Roo.get(document).on('mousewheel', this.collapseIf, this);
42963             
42964             this.fireEvent('expand', this);
42965         },
42966         
42967         restrictHeight : function()
42968         {
42969             this.list.alignTo(this.inputEl(), this.listAlign);
42970             this.list.alignTo(this.inputEl(), this.listAlign);
42971         },
42972         
42973         onViewOver : function(e, t)
42974         {
42975             if(this.inKeyMode){
42976                 return;
42977             }
42978             var item = this.view.findItemFromChild(t);
42979             
42980             if(item){
42981                 var index = this.view.indexOf(item);
42982                 this.select(index, false);
42983             }
42984         },
42985
42986         // private
42987         onViewClick : function(view, doFocus, el, e)
42988         {
42989             var index = this.view.getSelectedIndexes()[0];
42990             
42991             var r = this.store.getAt(index);
42992             
42993             if(r){
42994                 this.onSelect(r, index);
42995             }
42996             if(doFocus !== false && !this.blockFocus){
42997                 this.inputEl().focus();
42998             }
42999         },
43000         
43001         onViewMove : function(e, t)
43002         {
43003             this.inKeyMode = false;
43004         },
43005         
43006         select : function(index, scrollIntoView)
43007         {
43008             this.selectedIndex = index;
43009             this.view.select(index);
43010             if(scrollIntoView !== false){
43011                 var el = this.view.getNode(index);
43012                 if(el){
43013                     this.list.scrollChildIntoView(el, false);
43014                 }
43015             }
43016         },
43017         
43018         createList : function()
43019         {
43020             this.list = Roo.get(document.body).createChild({
43021                 tag: 'ul',
43022                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43023                 style: 'display:none'
43024             });
43025             
43026             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43027         },
43028         
43029         collapseIf : function(e)
43030         {
43031             var in_combo  = e.within(this.el);
43032             var in_list =  e.within(this.list);
43033             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43034             
43035             if (in_combo || in_list || is_list) {
43036                 return;
43037             }
43038             this.collapse();
43039         },
43040         
43041         onSelect : function(record, index)
43042         {
43043             if(this.fireEvent('beforeselect', this, record, index) !== false){
43044                 
43045                 this.setFlagClass(record.data.iso2);
43046                 this.setDialCode(record.data.dialCode);
43047                 this.hasFocus = false;
43048                 this.collapse();
43049                 this.fireEvent('select', this, record, index);
43050             }
43051         },
43052         
43053         flagEl : function()
43054         {
43055             var flag = this.el.select('div.flag',true).first();
43056             if(!flag){
43057                 return false;
43058             }
43059             return flag;
43060         },
43061         
43062         dialCodeHolderEl : function()
43063         {
43064             var d = this.el.select('input.dial-code-holder',true).first();
43065             if(!d){
43066                 return false;
43067             }
43068             return d;
43069         },
43070         
43071         setDialCode : function(v)
43072         {
43073             this.dialCodeHolder.dom.value = '+'+v;
43074         },
43075         
43076         setFlagClass : function(n)
43077         {
43078             this.flag.dom.className = 'flag '+n;
43079         },
43080         
43081         getValue : function()
43082         {
43083             var v = this.inputEl().getValue();
43084             if(this.dialCodeHolder) {
43085                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43086             }
43087             return v;
43088         },
43089         
43090         setValue : function(v)
43091         {
43092             var d = this.getDialCode(v);
43093             
43094             //invalid dial code
43095             if(v.length == 0 || !d || d.length == 0) {
43096                 if(this.rendered){
43097                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43098                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43099                 }
43100                 return;
43101             }
43102             
43103             //valid dial code
43104             this.setFlagClass(this.dialCodeMapping[d].iso2);
43105             this.setDialCode(d);
43106             this.inputEl().dom.value = v.replace('+'+d,'');
43107             this.hiddenEl().dom.value = this.getValue();
43108             
43109             this.validate();
43110         },
43111         
43112         getDialCode : function(v)
43113         {
43114             v = v ||  '';
43115             
43116             if (v.length == 0) {
43117                 return this.dialCodeHolder.dom.value;
43118             }
43119             
43120             var dialCode = "";
43121             if (v.charAt(0) != "+") {
43122                 return false;
43123             }
43124             var numericChars = "";
43125             for (var i = 1; i < v.length; i++) {
43126               var c = v.charAt(i);
43127               if (!isNaN(c)) {
43128                 numericChars += c;
43129                 if (this.dialCodeMapping[numericChars]) {
43130                   dialCode = v.substr(1, i);
43131                 }
43132                 if (numericChars.length == 4) {
43133                   break;
43134                 }
43135               }
43136             }
43137             return dialCode;
43138         },
43139         
43140         reset : function()
43141         {
43142             this.setValue(this.defaultDialCode);
43143             this.validate();
43144         },
43145         
43146         hiddenEl : function()
43147         {
43148             return this.el.select('input.hidden-tel-input',true).first();
43149         },
43150         
43151         // after setting val
43152         onKeyUp : function(e){
43153             this.setValue(this.getValue());
43154         },
43155         
43156         onKeyPress : function(e){
43157             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43158                 e.stopEvent();
43159             }
43160         }
43161         
43162 });
43163 /**
43164  * @class Roo.bootstrap.MoneyField
43165  * @extends Roo.bootstrap.ComboBox
43166  * Bootstrap MoneyField class
43167  * 
43168  * @constructor
43169  * Create a new MoneyField.
43170  * @param {Object} config Configuration options
43171  */
43172
43173 Roo.bootstrap.MoneyField = function(config) {
43174     
43175     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43176     
43177 };
43178
43179 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43180     
43181     /**
43182      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43183      */
43184     allowDecimals : true,
43185     /**
43186      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43187      */
43188     decimalSeparator : ".",
43189     /**
43190      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43191      */
43192     decimalPrecision : 0,
43193     /**
43194      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43195      */
43196     allowNegative : true,
43197     /**
43198      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43199      */
43200     allowZero: true,
43201     /**
43202      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43203      */
43204     minValue : Number.NEGATIVE_INFINITY,
43205     /**
43206      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43207      */
43208     maxValue : Number.MAX_VALUE,
43209     /**
43210      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43211      */
43212     minText : "The minimum value for this field is {0}",
43213     /**
43214      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43215      */
43216     maxText : "The maximum value for this field is {0}",
43217     /**
43218      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43219      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43220      */
43221     nanText : "{0} is not a valid number",
43222     /**
43223      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43224      */
43225     castInt : true,
43226     /**
43227      * @cfg {String} defaults currency of the MoneyField
43228      * value should be in lkey
43229      */
43230     defaultCurrency : false,
43231     /**
43232      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43233      */
43234     thousandsDelimiter : false,
43235     /**
43236      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43237      */
43238     max_length: false,
43239     
43240     inputlg : 9,
43241     inputmd : 9,
43242     inputsm : 9,
43243     inputxs : 6,
43244     
43245     store : false,
43246     
43247     getAutoCreate : function()
43248     {
43249         var align = this.labelAlign || this.parentLabelAlign();
43250         
43251         var id = Roo.id();
43252
43253         var cfg = {
43254             cls: 'form-group',
43255             cn: []
43256         };
43257
43258         var input =  {
43259             tag: 'input',
43260             id : id,
43261             cls : 'form-control roo-money-amount-input',
43262             autocomplete: 'new-password'
43263         };
43264         
43265         var hiddenInput = {
43266             tag: 'input',
43267             type: 'hidden',
43268             id: Roo.id(),
43269             cls: 'hidden-number-input'
43270         };
43271         
43272         if(this.max_length) {
43273             input.maxlength = this.max_length; 
43274         }
43275         
43276         if (this.name) {
43277             hiddenInput.name = this.name;
43278         }
43279
43280         if (this.disabled) {
43281             input.disabled = true;
43282         }
43283
43284         var clg = 12 - this.inputlg;
43285         var cmd = 12 - this.inputmd;
43286         var csm = 12 - this.inputsm;
43287         var cxs = 12 - this.inputxs;
43288         
43289         var container = {
43290             tag : 'div',
43291             cls : 'row roo-money-field',
43292             cn : [
43293                 {
43294                     tag : 'div',
43295                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43296                     cn : [
43297                         {
43298                             tag : 'div',
43299                             cls: 'roo-select2-container input-group',
43300                             cn: [
43301                                 {
43302                                     tag : 'input',
43303                                     cls : 'form-control roo-money-currency-input',
43304                                     autocomplete: 'new-password',
43305                                     readOnly : 1,
43306                                     name : this.currencyName
43307                                 },
43308                                 {
43309                                     tag :'span',
43310                                     cls : 'input-group-addon',
43311                                     cn : [
43312                                         {
43313                                             tag: 'span',
43314                                             cls: 'caret'
43315                                         }
43316                                     ]
43317                                 }
43318                             ]
43319                         }
43320                     ]
43321                 },
43322                 {
43323                     tag : 'div',
43324                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43325                     cn : [
43326                         {
43327                             tag: 'div',
43328                             cls: this.hasFeedback ? 'has-feedback' : '',
43329                             cn: [
43330                                 input
43331                             ]
43332                         }
43333                     ]
43334                 }
43335             ]
43336             
43337         };
43338         
43339         if (this.fieldLabel.length) {
43340             var indicator = {
43341                 tag: 'i',
43342                 tooltip: 'This field is required'
43343             };
43344
43345             var label = {
43346                 tag: 'label',
43347                 'for':  id,
43348                 cls: 'control-label',
43349                 cn: []
43350             };
43351
43352             var label_text = {
43353                 tag: 'span',
43354                 html: this.fieldLabel
43355             };
43356
43357             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43358             label.cn = [
43359                 indicator,
43360                 label_text
43361             ];
43362
43363             if(this.indicatorpos == 'right') {
43364                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43365                 label.cn = [
43366                     label_text,
43367                     indicator
43368                 ];
43369             }
43370
43371             if(align == 'left') {
43372                 container = {
43373                     tag: 'div',
43374                     cn: [
43375                         container
43376                     ]
43377                 };
43378
43379                 if(this.labelWidth > 12){
43380                     label.style = "width: " + this.labelWidth + 'px';
43381                 }
43382                 if(this.labelWidth < 13 && this.labelmd == 0){
43383                     this.labelmd = this.labelWidth;
43384                 }
43385                 if(this.labellg > 0){
43386                     label.cls += ' col-lg-' + this.labellg;
43387                     input.cls += ' col-lg-' + (12 - this.labellg);
43388                 }
43389                 if(this.labelmd > 0){
43390                     label.cls += ' col-md-' + this.labelmd;
43391                     container.cls += ' col-md-' + (12 - this.labelmd);
43392                 }
43393                 if(this.labelsm > 0){
43394                     label.cls += ' col-sm-' + this.labelsm;
43395                     container.cls += ' col-sm-' + (12 - this.labelsm);
43396                 }
43397                 if(this.labelxs > 0){
43398                     label.cls += ' col-xs-' + this.labelxs;
43399                     container.cls += ' col-xs-' + (12 - this.labelxs);
43400                 }
43401             }
43402         }
43403
43404         cfg.cn = [
43405             label,
43406             container,
43407             hiddenInput
43408         ];
43409         
43410         var settings = this;
43411
43412         ['xs','sm','md','lg'].map(function(size){
43413             if (settings[size]) {
43414                 cfg.cls += ' col-' + size + '-' + settings[size];
43415             }
43416         });
43417         
43418         return cfg;
43419     },
43420     
43421     initEvents : function()
43422     {
43423         this.indicator = this.indicatorEl();
43424         
43425         this.initCurrencyEvent();
43426         
43427         this.initNumberEvent();
43428     },
43429     
43430     initCurrencyEvent : function()
43431     {
43432         if (!this.store) {
43433             throw "can not find store for combo";
43434         }
43435         
43436         this.store = Roo.factory(this.store, Roo.data);
43437         this.store.parent = this;
43438         
43439         this.createList();
43440         
43441         this.triggerEl = this.el.select('.input-group-addon', true).first();
43442         
43443         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43444         
43445         var _this = this;
43446         
43447         (function(){
43448             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43449             _this.list.setWidth(lw);
43450         }).defer(100);
43451         
43452         this.list.on('mouseover', this.onViewOver, this);
43453         this.list.on('mousemove', this.onViewMove, this);
43454         this.list.on('scroll', this.onViewScroll, this);
43455         
43456         if(!this.tpl){
43457             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43458         }
43459         
43460         this.view = new Roo.View(this.list, this.tpl, {
43461             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43462         });
43463         
43464         this.view.on('click', this.onViewClick, this);
43465         
43466         this.store.on('beforeload', this.onBeforeLoad, this);
43467         this.store.on('load', this.onLoad, this);
43468         this.store.on('loadexception', this.onLoadException, this);
43469         
43470         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43471             "up" : function(e){
43472                 this.inKeyMode = true;
43473                 this.selectPrev();
43474             },
43475
43476             "down" : function(e){
43477                 if(!this.isExpanded()){
43478                     this.onTriggerClick();
43479                 }else{
43480                     this.inKeyMode = true;
43481                     this.selectNext();
43482                 }
43483             },
43484
43485             "enter" : function(e){
43486                 this.collapse();
43487                 
43488                 if(this.fireEvent("specialkey", this, e)){
43489                     this.onViewClick(false);
43490                 }
43491                 
43492                 return true;
43493             },
43494
43495             "esc" : function(e){
43496                 this.collapse();
43497             },
43498
43499             "tab" : function(e){
43500                 this.collapse();
43501                 
43502                 if(this.fireEvent("specialkey", this, e)){
43503                     this.onViewClick(false);
43504                 }
43505                 
43506                 return true;
43507             },
43508
43509             scope : this,
43510
43511             doRelay : function(foo, bar, hname){
43512                 if(hname == 'down' || this.scope.isExpanded()){
43513                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43514                 }
43515                 return true;
43516             },
43517
43518             forceKeyDown: true
43519         });
43520         
43521         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43522         
43523     },
43524     
43525     initNumberEvent : function(e)
43526     {
43527         this.inputEl().on("keydown" , this.fireKey,  this);
43528         this.inputEl().on("focus", this.onFocus,  this);
43529         this.inputEl().on("blur", this.onBlur,  this);
43530         
43531         this.inputEl().relayEvent('keyup', this);
43532         
43533         if(this.indicator){
43534             this.indicator.addClass('invisible');
43535         }
43536  
43537         this.originalValue = this.getValue();
43538         
43539         if(this.validationEvent == 'keyup'){
43540             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43541             this.inputEl().on('keyup', this.filterValidation, this);
43542         }
43543         else if(this.validationEvent !== false){
43544             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43545         }
43546         
43547         if(this.selectOnFocus){
43548             this.on("focus", this.preFocus, this);
43549             
43550         }
43551         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43552             this.inputEl().on("keypress", this.filterKeys, this);
43553         } else {
43554             this.inputEl().relayEvent('keypress', this);
43555         }
43556         
43557         var allowed = "0123456789";
43558         
43559         if(this.allowDecimals){
43560             allowed += this.decimalSeparator;
43561         }
43562         
43563         if(this.allowNegative){
43564             allowed += "-";
43565         }
43566         
43567         if(this.thousandsDelimiter) {
43568             allowed += ",";
43569         }
43570         
43571         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43572         
43573         var keyPress = function(e){
43574             
43575             var k = e.getKey();
43576             
43577             var c = e.getCharCode();
43578             
43579             if(
43580                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43581                     allowed.indexOf(String.fromCharCode(c)) === -1
43582             ){
43583                 e.stopEvent();
43584                 return;
43585             }
43586             
43587             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43588                 return;
43589             }
43590             
43591             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43592                 e.stopEvent();
43593             }
43594         };
43595         
43596         this.inputEl().on("keypress", keyPress, this);
43597         
43598     },
43599     
43600     onTriggerClick : function(e)
43601     {   
43602         if(this.disabled){
43603             return;
43604         }
43605         
43606         this.page = 0;
43607         this.loadNext = false;
43608         
43609         if(this.isExpanded()){
43610             this.collapse();
43611             return;
43612         }
43613         
43614         this.hasFocus = true;
43615         
43616         if(this.triggerAction == 'all') {
43617             this.doQuery(this.allQuery, true);
43618             return;
43619         }
43620         
43621         this.doQuery(this.getRawValue());
43622     },
43623     
43624     getCurrency : function()
43625     {   
43626         var v = this.currencyEl().getValue();
43627         
43628         return v;
43629     },
43630     
43631     restrictHeight : function()
43632     {
43633         this.list.alignTo(this.currencyEl(), this.listAlign);
43634         this.list.alignTo(this.currencyEl(), this.listAlign);
43635     },
43636     
43637     onViewClick : function(view, doFocus, el, e)
43638     {
43639         var index = this.view.getSelectedIndexes()[0];
43640         
43641         var r = this.store.getAt(index);
43642         
43643         if(r){
43644             this.onSelect(r, index);
43645         }
43646     },
43647     
43648     onSelect : function(record, index){
43649         
43650         if(this.fireEvent('beforeselect', this, record, index) !== false){
43651         
43652             this.setFromCurrencyData(index > -1 ? record.data : false);
43653             
43654             this.collapse();
43655             
43656             this.fireEvent('select', this, record, index);
43657         }
43658     },
43659     
43660     setFromCurrencyData : function(o)
43661     {
43662         var currency = '';
43663         
43664         this.lastCurrency = o;
43665         
43666         if (this.currencyField) {
43667             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43668         } else {
43669             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43670         }
43671         
43672         this.lastSelectionText = currency;
43673         
43674         //setting default currency
43675         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43676             this.setCurrency(this.defaultCurrency);
43677             return;
43678         }
43679         
43680         this.setCurrency(currency);
43681     },
43682     
43683     setFromData : function(o)
43684     {
43685         var c = {};
43686         
43687         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43688         
43689         this.setFromCurrencyData(c);
43690         
43691         var value = '';
43692         
43693         if (this.name) {
43694             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43695         } else {
43696             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43697         }
43698         
43699         this.setValue(value);
43700         
43701     },
43702     
43703     setCurrency : function(v)
43704     {   
43705         this.currencyValue = v;
43706         
43707         if(this.rendered){
43708             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43709             this.validate();
43710         }
43711     },
43712     
43713     setValue : function(v)
43714     {
43715         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43716         
43717         this.value = v;
43718         
43719         if(this.rendered){
43720             
43721             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43722             
43723             this.inputEl().dom.value = (v == '') ? '' :
43724                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43725             
43726             if(!this.allowZero && v === '0') {
43727                 this.hiddenEl().dom.value = '';
43728                 this.inputEl().dom.value = '';
43729             }
43730             
43731             this.validate();
43732         }
43733     },
43734     
43735     getRawValue : function()
43736     {
43737         var v = this.inputEl().getValue();
43738         
43739         return v;
43740     },
43741     
43742     getValue : function()
43743     {
43744         return this.fixPrecision(this.parseValue(this.getRawValue()));
43745     },
43746     
43747     parseValue : function(value)
43748     {
43749         if(this.thousandsDelimiter) {
43750             value += "";
43751             r = new RegExp(",", "g");
43752             value = value.replace(r, "");
43753         }
43754         
43755         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43756         return isNaN(value) ? '' : value;
43757         
43758     },
43759     
43760     fixPrecision : function(value)
43761     {
43762         if(this.thousandsDelimiter) {
43763             value += "";
43764             r = new RegExp(",", "g");
43765             value = value.replace(r, "");
43766         }
43767         
43768         var nan = isNaN(value);
43769         
43770         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43771             return nan ? '' : value;
43772         }
43773         return parseFloat(value).toFixed(this.decimalPrecision);
43774     },
43775     
43776     decimalPrecisionFcn : function(v)
43777     {
43778         return Math.floor(v);
43779     },
43780     
43781     validateValue : function(value)
43782     {
43783         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43784             return false;
43785         }
43786         
43787         var num = this.parseValue(value);
43788         
43789         if(isNaN(num)){
43790             this.markInvalid(String.format(this.nanText, value));
43791             return false;
43792         }
43793         
43794         if(num < this.minValue){
43795             this.markInvalid(String.format(this.minText, this.minValue));
43796             return false;
43797         }
43798         
43799         if(num > this.maxValue){
43800             this.markInvalid(String.format(this.maxText, this.maxValue));
43801             return false;
43802         }
43803         
43804         return true;
43805     },
43806     
43807     validate : function()
43808     {
43809         if(this.disabled || this.allowBlank){
43810             this.markValid();
43811             return true;
43812         }
43813         
43814         var currency = this.getCurrency();
43815         
43816         if(this.validateValue(this.getRawValue()) && currency.length){
43817             this.markValid();
43818             return true;
43819         }
43820         
43821         this.markInvalid();
43822         return false;
43823     },
43824     
43825     getName: function()
43826     {
43827         return this.name;
43828     },
43829     
43830     beforeBlur : function()
43831     {
43832         if(!this.castInt){
43833             return;
43834         }
43835         
43836         var v = this.parseValue(this.getRawValue());
43837         
43838         if(v || v == 0){
43839             this.setValue(v);
43840         }
43841     },
43842     
43843     onBlur : function()
43844     {
43845         this.beforeBlur();
43846         
43847         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43848             //this.el.removeClass(this.focusClass);
43849         }
43850         
43851         this.hasFocus = false;
43852         
43853         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43854             this.validate();
43855         }
43856         
43857         var v = this.getValue();
43858         
43859         if(String(v) !== String(this.startValue)){
43860             this.fireEvent('change', this, v, this.startValue);
43861         }
43862         
43863         this.fireEvent("blur", this);
43864     },
43865     
43866     inputEl : function()
43867     {
43868         return this.el.select('.roo-money-amount-input', true).first();
43869     },
43870     
43871     currencyEl : function()
43872     {
43873         return this.el.select('.roo-money-currency-input', true).first();
43874     },
43875     
43876     hiddenEl : function()
43877     {
43878         return this.el.select('input.hidden-number-input',true).first();
43879     }
43880     
43881 });/**
43882  * @class Roo.bootstrap.BezierSignature
43883  * @extends Roo.bootstrap.Component
43884  * Bootstrap BezierSignature class
43885  * This script refer to:
43886  *    Title: Signature Pad
43887  *    Author: szimek
43888  *    Availability: https://github.com/szimek/signature_pad
43889  *
43890  * @constructor
43891  * Create a new BezierSignature
43892  * @param {Object} config The config object
43893  */
43894
43895 Roo.bootstrap.BezierSignature = function(config){
43896     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43897     this.addEvents({
43898         "resize" : true
43899     });
43900 };
43901
43902 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43903 {
43904      
43905     curve_data: [],
43906     
43907     is_empty: true,
43908     
43909     mouse_btn_down: true,
43910     
43911     /**
43912      * @cfg {int} canvas height
43913      */
43914     canvas_height: '200px',
43915     
43916     /**
43917      * @cfg {float|function} Radius of a single dot.
43918      */ 
43919     dot_size: false,
43920     
43921     /**
43922      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43923      */
43924     min_width: 0.5,
43925     
43926     /**
43927      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43928      */
43929     max_width: 2.5,
43930     
43931     /**
43932      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43933      */
43934     throttle: 16,
43935     
43936     /**
43937      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43938      */
43939     min_distance: 5,
43940     
43941     /**
43942      * @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.
43943      */
43944     bg_color: 'rgba(0, 0, 0, 0)',
43945     
43946     /**
43947      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43948      */
43949     dot_color: 'black',
43950     
43951     /**
43952      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43953      */ 
43954     velocity_filter_weight: 0.7,
43955     
43956     /**
43957      * @cfg {function} Callback when stroke begin. 
43958      */
43959     onBegin: false,
43960     
43961     /**
43962      * @cfg {function} Callback when stroke end.
43963      */
43964     onEnd: false,
43965     
43966     getAutoCreate : function()
43967     {
43968         var cls = 'roo-signature column';
43969         
43970         if(this.cls){
43971             cls += ' ' + this.cls;
43972         }
43973         
43974         var col_sizes = [
43975             'lg',
43976             'md',
43977             'sm',
43978             'xs'
43979         ];
43980         
43981         for(var i = 0; i < col_sizes.length; i++) {
43982             if(this[col_sizes[i]]) {
43983                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43984             }
43985         }
43986         
43987         var cfg = {
43988             tag: 'div',
43989             cls: cls,
43990             cn: [
43991                 {
43992                     tag: 'div',
43993                     cls: 'roo-signature-body',
43994                     cn: [
43995                         {
43996                             tag: 'canvas',
43997                             cls: 'roo-signature-body-canvas',
43998                             height: this.canvas_height,
43999                             width: this.canvas_width
44000                         }
44001                     ]
44002                 },
44003                 {
44004                     tag: 'input',
44005                     type: 'file',
44006                     style: 'display: none'
44007                 }
44008             ]
44009         };
44010         
44011         return cfg;
44012     },
44013     
44014     initEvents: function() 
44015     {
44016         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44017         
44018         var canvas = this.canvasEl();
44019         
44020         // mouse && touch event swapping...
44021         canvas.dom.style.touchAction = 'none';
44022         canvas.dom.style.msTouchAction = 'none';
44023         
44024         this.mouse_btn_down = false;
44025         canvas.on('mousedown', this._handleMouseDown, this);
44026         canvas.on('mousemove', this._handleMouseMove, this);
44027         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44028         
44029         if (window.PointerEvent) {
44030             canvas.on('pointerdown', this._handleMouseDown, this);
44031             canvas.on('pointermove', this._handleMouseMove, this);
44032             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44033         }
44034         
44035         if ('ontouchstart' in window) {
44036             canvas.on('touchstart', this._handleTouchStart, this);
44037             canvas.on('touchmove', this._handleTouchMove, this);
44038             canvas.on('touchend', this._handleTouchEnd, this);
44039         }
44040         
44041         Roo.EventManager.onWindowResize(this.resize, this, true);
44042         
44043         // file input event
44044         this.fileEl().on('change', this.uploadImage, this);
44045         
44046         this.clear();
44047         
44048         this.resize();
44049     },
44050     
44051     resize: function(){
44052         
44053         var canvas = this.canvasEl().dom;
44054         var ctx = this.canvasElCtx();
44055         var img_data = false;
44056         
44057         if(canvas.width > 0) {
44058             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44059         }
44060         // setting canvas width will clean img data
44061         canvas.width = 0;
44062         
44063         var style = window.getComputedStyle ? 
44064             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44065             
44066         var padding_left = parseInt(style.paddingLeft) || 0;
44067         var padding_right = parseInt(style.paddingRight) || 0;
44068         
44069         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44070         
44071         if(img_data) {
44072             ctx.putImageData(img_data, 0, 0);
44073         }
44074     },
44075     
44076     _handleMouseDown: function(e)
44077     {
44078         if (e.browserEvent.which === 1) {
44079             this.mouse_btn_down = true;
44080             this.strokeBegin(e);
44081         }
44082     },
44083     
44084     _handleMouseMove: function (e)
44085     {
44086         if (this.mouse_btn_down) {
44087             this.strokeMoveUpdate(e);
44088         }
44089     },
44090     
44091     _handleMouseUp: function (e)
44092     {
44093         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44094             this.mouse_btn_down = false;
44095             this.strokeEnd(e);
44096         }
44097     },
44098     
44099     _handleTouchStart: function (e) {
44100         
44101         e.preventDefault();
44102         if (e.browserEvent.targetTouches.length === 1) {
44103             // var touch = e.browserEvent.changedTouches[0];
44104             // this.strokeBegin(touch);
44105             
44106              this.strokeBegin(e); // assume e catching the correct xy...
44107         }
44108     },
44109     
44110     _handleTouchMove: function (e) {
44111         e.preventDefault();
44112         // var touch = event.targetTouches[0];
44113         // _this._strokeMoveUpdate(touch);
44114         this.strokeMoveUpdate(e);
44115     },
44116     
44117     _handleTouchEnd: function (e) {
44118         var wasCanvasTouched = e.target === this.canvasEl().dom;
44119         if (wasCanvasTouched) {
44120             e.preventDefault();
44121             // var touch = event.changedTouches[0];
44122             // _this._strokeEnd(touch);
44123             this.strokeEnd(e);
44124         }
44125     },
44126     
44127     reset: function () {
44128         this._lastPoints = [];
44129         this._lastVelocity = 0;
44130         this._lastWidth = (this.min_width + this.max_width) / 2;
44131         this.canvasElCtx().fillStyle = this.dot_color;
44132     },
44133     
44134     strokeMoveUpdate: function(e)
44135     {
44136         this.strokeUpdate(e);
44137         
44138         if (this.throttle) {
44139             this.throttleStroke(this.strokeUpdate, this.throttle);
44140         }
44141         else {
44142             this.strokeUpdate(e);
44143         }
44144     },
44145     
44146     strokeBegin: function(e)
44147     {
44148         var newPointGroup = {
44149             color: this.dot_color,
44150             points: []
44151         };
44152         
44153         if (typeof this.onBegin === 'function') {
44154             this.onBegin(e);
44155         }
44156         
44157         this.curve_data.push(newPointGroup);
44158         this.reset();
44159         this.strokeUpdate(e);
44160     },
44161     
44162     strokeUpdate: function(e)
44163     {
44164         var rect = this.canvasEl().dom.getBoundingClientRect();
44165         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44166         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44167         var lastPoints = lastPointGroup.points;
44168         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44169         var isLastPointTooClose = lastPoint
44170             ? point.distanceTo(lastPoint) <= this.min_distance
44171             : false;
44172         var color = lastPointGroup.color;
44173         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44174             var curve = this.addPoint(point);
44175             if (!lastPoint) {
44176                 this.drawDot({color: color, point: point});
44177             }
44178             else if (curve) {
44179                 this.drawCurve({color: color, curve: curve});
44180             }
44181             lastPoints.push({
44182                 time: point.time,
44183                 x: point.x,
44184                 y: point.y
44185             });
44186         }
44187     },
44188     
44189     strokeEnd: function(e)
44190     {
44191         this.strokeUpdate(e);
44192         if (typeof this.onEnd === 'function') {
44193             this.onEnd(e);
44194         }
44195     },
44196     
44197     addPoint:  function (point) {
44198         var _lastPoints = this._lastPoints;
44199         _lastPoints.push(point);
44200         if (_lastPoints.length > 2) {
44201             if (_lastPoints.length === 3) {
44202                 _lastPoints.unshift(_lastPoints[0]);
44203             }
44204             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44205             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44206             _lastPoints.shift();
44207             return curve;
44208         }
44209         return null;
44210     },
44211     
44212     calculateCurveWidths: function (startPoint, endPoint) {
44213         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44214             (1 - this.velocity_filter_weight) * this._lastVelocity;
44215
44216         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44217         var widths = {
44218             end: newWidth,
44219             start: this._lastWidth
44220         };
44221         
44222         this._lastVelocity = velocity;
44223         this._lastWidth = newWidth;
44224         return widths;
44225     },
44226     
44227     drawDot: function (_a) {
44228         var color = _a.color, point = _a.point;
44229         var ctx = this.canvasElCtx();
44230         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44231         ctx.beginPath();
44232         this.drawCurveSegment(point.x, point.y, width);
44233         ctx.closePath();
44234         ctx.fillStyle = color;
44235         ctx.fill();
44236     },
44237     
44238     drawCurve: function (_a) {
44239         var color = _a.color, curve = _a.curve;
44240         var ctx = this.canvasElCtx();
44241         var widthDelta = curve.endWidth - curve.startWidth;
44242         var drawSteps = Math.floor(curve.length()) * 2;
44243         ctx.beginPath();
44244         ctx.fillStyle = color;
44245         for (var i = 0; i < drawSteps; i += 1) {
44246         var t = i / drawSteps;
44247         var tt = t * t;
44248         var ttt = tt * t;
44249         var u = 1 - t;
44250         var uu = u * u;
44251         var uuu = uu * u;
44252         var x = uuu * curve.startPoint.x;
44253         x += 3 * uu * t * curve.control1.x;
44254         x += 3 * u * tt * curve.control2.x;
44255         x += ttt * curve.endPoint.x;
44256         var y = uuu * curve.startPoint.y;
44257         y += 3 * uu * t * curve.control1.y;
44258         y += 3 * u * tt * curve.control2.y;
44259         y += ttt * curve.endPoint.y;
44260         var width = curve.startWidth + ttt * widthDelta;
44261         this.drawCurveSegment(x, y, width);
44262         }
44263         ctx.closePath();
44264         ctx.fill();
44265     },
44266     
44267     drawCurveSegment: function (x, y, width) {
44268         var ctx = this.canvasElCtx();
44269         ctx.moveTo(x, y);
44270         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44271         this.is_empty = false;
44272     },
44273     
44274     clear: function()
44275     {
44276         var ctx = this.canvasElCtx();
44277         var canvas = this.canvasEl().dom;
44278         ctx.fillStyle = this.bg_color;
44279         ctx.clearRect(0, 0, canvas.width, canvas.height);
44280         ctx.fillRect(0, 0, canvas.width, canvas.height);
44281         this.curve_data = [];
44282         this.reset();
44283         this.is_empty = true;
44284     },
44285     
44286     fileEl: function()
44287     {
44288         return  this.el.select('input',true).first();
44289     },
44290     
44291     canvasEl: function()
44292     {
44293         return this.el.select('canvas',true).first();
44294     },
44295     
44296     canvasElCtx: function()
44297     {
44298         return this.el.select('canvas',true).first().dom.getContext('2d');
44299     },
44300     
44301     getImage: function(type)
44302     {
44303         if(this.is_empty) {
44304             return false;
44305         }
44306         
44307         // encryption ?
44308         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44309     },
44310     
44311     drawFromImage: function(img_src)
44312     {
44313         var img = new Image();
44314         
44315         img.onload = function(){
44316             this.canvasElCtx().drawImage(img, 0, 0);
44317         }.bind(this);
44318         
44319         img.src = img_src;
44320         
44321         this.is_empty = false;
44322     },
44323     
44324     selectImage: function()
44325     {
44326         this.fileEl().dom.click();
44327     },
44328     
44329     uploadImage: function(e)
44330     {
44331         var reader = new FileReader();
44332         
44333         reader.onload = function(e){
44334             var img = new Image();
44335             img.onload = function(){
44336                 this.reset();
44337                 this.canvasElCtx().drawImage(img, 0, 0);
44338             }.bind(this);
44339             img.src = e.target.result;
44340         }.bind(this);
44341         
44342         reader.readAsDataURL(e.target.files[0]);
44343     },
44344     
44345     // Bezier Point Constructor
44346     Point: (function () {
44347         function Point(x, y, time) {
44348             this.x = x;
44349             this.y = y;
44350             this.time = time || Date.now();
44351         }
44352         Point.prototype.distanceTo = function (start) {
44353             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44354         };
44355         Point.prototype.equals = function (other) {
44356             return this.x === other.x && this.y === other.y && this.time === other.time;
44357         };
44358         Point.prototype.velocityFrom = function (start) {
44359             return this.time !== start.time
44360             ? this.distanceTo(start) / (this.time - start.time)
44361             : 0;
44362         };
44363         return Point;
44364     }()),
44365     
44366     
44367     // Bezier Constructor
44368     Bezier: (function () {
44369         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44370             this.startPoint = startPoint;
44371             this.control2 = control2;
44372             this.control1 = control1;
44373             this.endPoint = endPoint;
44374             this.startWidth = startWidth;
44375             this.endWidth = endWidth;
44376         }
44377         Bezier.fromPoints = function (points, widths, scope) {
44378             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44379             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44380             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44381         };
44382         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44383             var dx1 = s1.x - s2.x;
44384             var dy1 = s1.y - s2.y;
44385             var dx2 = s2.x - s3.x;
44386             var dy2 = s2.y - s3.y;
44387             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44388             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44389             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44390             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44391             var dxm = m1.x - m2.x;
44392             var dym = m1.y - m2.y;
44393             var k = l2 / (l1 + l2);
44394             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44395             var tx = s2.x - cm.x;
44396             var ty = s2.y - cm.y;
44397             return {
44398                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44399                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44400             };
44401         };
44402         Bezier.prototype.length = function () {
44403             var steps = 10;
44404             var length = 0;
44405             var px;
44406             var py;
44407             for (var i = 0; i <= steps; i += 1) {
44408                 var t = i / steps;
44409                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44410                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44411                 if (i > 0) {
44412                     var xdiff = cx - px;
44413                     var ydiff = cy - py;
44414                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44415                 }
44416                 px = cx;
44417                 py = cy;
44418             }
44419             return length;
44420         };
44421         Bezier.prototype.point = function (t, start, c1, c2, end) {
44422             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44423             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44424             + (3.0 * c2 * (1.0 - t) * t * t)
44425             + (end * t * t * t);
44426         };
44427         return Bezier;
44428     }()),
44429     
44430     throttleStroke: function(fn, wait) {
44431       if (wait === void 0) { wait = 250; }
44432       var previous = 0;
44433       var timeout = null;
44434       var result;
44435       var storedContext;
44436       var storedArgs;
44437       var later = function () {
44438           previous = Date.now();
44439           timeout = null;
44440           result = fn.apply(storedContext, storedArgs);
44441           if (!timeout) {
44442               storedContext = null;
44443               storedArgs = [];
44444           }
44445       };
44446       return function wrapper() {
44447           var args = [];
44448           for (var _i = 0; _i < arguments.length; _i++) {
44449               args[_i] = arguments[_i];
44450           }
44451           var now = Date.now();
44452           var remaining = wait - (now - previous);
44453           storedContext = this;
44454           storedArgs = args;
44455           if (remaining <= 0 || remaining > wait) {
44456               if (timeout) {
44457                   clearTimeout(timeout);
44458                   timeout = null;
44459               }
44460               previous = now;
44461               result = fn.apply(storedContext, storedArgs);
44462               if (!timeout) {
44463                   storedContext = null;
44464                   storedArgs = [];
44465               }
44466           }
44467           else if (!timeout) {
44468               timeout = window.setTimeout(later, remaining);
44469           }
44470           return result;
44471       };
44472   }
44473   
44474 });
44475
44476  
44477
44478