c9c535d82625444dc2e463b3be3b7fa96b785ad2
[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                     nane : this.name,
12532                     value : this.value,
12533                     cls : 'd-none  form-control'
12534                 },
12535                 
12536                 {
12537                     tag: 'input',
12538                     multiple : 'multiple',
12539                     type : 'file',
12540                     cls : 'd-none  roo-card-upload-selector'
12541                 },
12542                 
12543                 {
12544                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12545                 },
12546                 {
12547                     cls : 'card-columns roo-card-uploader-container'
12548                 }
12549
12550             ]
12551         };
12552            
12553          
12554         return cfg;
12555     },
12556     
12557     getChildContainer : function() /// what children are added to.
12558     {
12559         return this.containerEl;
12560     },
12561    
12562     getButtonContainer : function() /// what children are added to.
12563     {
12564         return this.el.select(".roo-card-uploader-button-container").first();
12565     },
12566    
12567     initEvents : function()
12568     {
12569         
12570         Roo.bootstrap.Input.prototype.initEvents.call(this);
12571         
12572         var t = this;
12573         this.addxtype({
12574             xns: Roo.bootstrap,
12575
12576             xtype : 'Button',
12577             container_method : 'getButtonContainer' ,            
12578             html :  this.html, // fix changable?
12579             cls : 'w-100 ',
12580             listeners : {
12581                 'click' : function(btn, e) {
12582                     t.onClick(e);
12583                 }
12584             }
12585         });
12586         
12587         
12588         
12589         
12590         this.urlAPI = (window.createObjectURL && window) || 
12591                                 (window.URL && URL.revokeObjectURL && URL) || 
12592                                 (window.webkitURL && webkitURL);
12593                         
12594          
12595          
12596          
12597         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598         
12599         this.selectorEl.on('change', this.onFileSelected, this);
12600         if (this.images) {
12601             var t = this;
12602             this.images.forEach(function(img) {
12603                 t.addCard(img)
12604             });
12605             this.images = false;
12606         }
12607         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12608          
12609        
12610     },
12611     
12612    
12613     onClick : function(e)
12614     {
12615         e.preventDefault();
12616          
12617         this.selectorEl.dom.click();
12618          
12619     },
12620     
12621     onFileSelected : function(e)
12622     {
12623         e.preventDefault();
12624         
12625         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626             return;
12627         }
12628         
12629         Roo.each(this.selectorEl.dom.files, function(file){    
12630             this.addFile(file);
12631         }, this);
12632          
12633     },
12634     
12635       
12636     
12637       
12638     
12639     addFile : function(file)
12640     {
12641            
12642         if(typeof(file) === 'string'){
12643             throw "Add file by name?"; // should not happen
12644             return;
12645         }
12646         
12647         if(!file || !this.urlAPI){
12648             return;
12649         }
12650         
12651         // file;
12652         // file.type;
12653         
12654         var _this = this;
12655         
12656         
12657         var url = _this.urlAPI.createObjectURL( file);
12658            
12659         this.addCard({
12660             id : Roo.bootstrap.CardUploader.ID--,
12661             is_uploaded : false,
12662             src : url,
12663             title : file.name,
12664             mimetype : file.type,
12665             preview : false,
12666             is_deleted : 0
12667         })
12668         
12669     },
12670     
12671     addCard : function (data)
12672     {
12673         // hidden input element?
12674         // if the file is not an image...
12675         //then we need to use something other that and header_image
12676         var t = this;
12677         //   remove.....
12678         var footer = [
12679             {
12680                 xns : Roo.bootstrap,
12681                 xtype : 'CardFooter',
12682                 items: [
12683                     {
12684                         xns : Roo.bootstrap,
12685                         xtype : 'Element',
12686                         cls : 'd-flex',
12687                         items : [
12688                             
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 html : String.format("<small>{0}</small>", data.title),
12693                                 cls : 'col-11 text-left',
12694                                 size: 'sm',
12695                                 weight: 'link',
12696                                 fa : 'download',
12697                                 listeners : {
12698                                     click : function() {
12699                                         this.downloadCard(data.id)
12700                                     }
12701                                 }
12702                             },
12703                           
12704                             {
12705                                 xns : Roo.bootstrap,
12706                                 xtype : 'Button',
12707                                 
12708                                 size : 'sm',
12709                                 weight: 'danger',
12710                                 cls : 'col-1',
12711                                 fa : 'times',
12712                                 listeners : {
12713                                     click : function() {
12714                                         t.removeCard(data.id)
12715                                     }
12716                                 }
12717                             }
12718                         ]
12719                     }
12720                     
12721                 ] 
12722             }
12723             
12724         ];
12725         
12726         var cn = this.addxtype(
12727             {
12728                  
12729                 xns : Roo.bootstrap,
12730                 xtype : 'Card',
12731                 closeable : true,
12732                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12734                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12735                 data : data,
12736                 html : false,
12737                  
12738                 items : footer,
12739                 initEvents : function() {
12740                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12741                     this.imgEl = this.el.select('.card-img-top').first();
12742                     if (this.imgEl) {
12743                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744                         this.imgEl.set({ 'pointer' : 'cursor' });
12745                                   
12746                     }
12747                     
12748                   
12749                 }
12750                 
12751             }
12752         );
12753         // dont' really need ot update items.
12754         // this.items.push(cn);
12755         this.fileCollection.add(cn);
12756         
12757         var _t = this;
12758         var reader = new FileReader();
12759         reader.onloadend = function(evt) {  
12760             data.srcdata =  evt.target.result;
12761             _t.updateInput();
12762         };
12763         reader.readAsDataURL(data.src);
12764         
12765         
12766         
12767     },
12768     removeCard : function(id)
12769     {
12770         
12771         var card  = this.fileCollection.get(id);
12772         card.data.is_deleted = 1;
12773         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12774         this.fileCollection.remove(card);
12775         //this.items = this.items.filter(function(e) { return e != card });
12776         // dont' really need ot update items.
12777         card.el.dom.parentNode.removeChild(card.el.dom);
12778         
12779     },
12780     reset: function()
12781     {
12782         this.fileCollection.each(function(card) {
12783             card.el.dom.parentNode.removeChild(card.el.dom);    
12784         });
12785         this.fileCollection.clear();
12786         this.updateInput();
12787     },
12788     
12789     updateInput : function()
12790     {
12791         var i =0;
12792         var data = [];
12793         this.fileCollection.forEach(function(e) {
12794             data.push(e.data);
12795             
12796         });
12797         this.inputEl().dom.value = JSON.stringify(data);
12798         
12799         
12800         
12801     }
12802     
12803     
12804 });
12805
12806
12807 Roo.bootstrap.CardUploader.ID = -1;/*
12808  * Based on:
12809  * Ext JS Library 1.1.1
12810  * Copyright(c) 2006-2007, Ext JS, LLC.
12811  *
12812  * Originally Released Under LGPL - original licence link has changed is not relivant.
12813  *
12814  * Fork - LGPL
12815  * <script type="text/javascript">
12816  */
12817
12818
12819 /**
12820  * @class Roo.data.SortTypes
12821  * @singleton
12822  * Defines the default sorting (casting?) comparison functions used when sorting data.
12823  */
12824 Roo.data.SortTypes = {
12825     /**
12826      * Default sort that does nothing
12827      * @param {Mixed} s The value being converted
12828      * @return {Mixed} The comparison value
12829      */
12830     none : function(s){
12831         return s;
12832     },
12833     
12834     /**
12835      * The regular expression used to strip tags
12836      * @type {RegExp}
12837      * @property
12838      */
12839     stripTagsRE : /<\/?[^>]+>/gi,
12840     
12841     /**
12842      * Strips all HTML tags to sort on text only
12843      * @param {Mixed} s The value being converted
12844      * @return {String} The comparison value
12845      */
12846     asText : function(s){
12847         return String(s).replace(this.stripTagsRE, "");
12848     },
12849     
12850     /**
12851      * Strips all HTML tags to sort on text only - Case insensitive
12852      * @param {Mixed} s The value being converted
12853      * @return {String} The comparison value
12854      */
12855     asUCText : function(s){
12856         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12857     },
12858     
12859     /**
12860      * Case insensitive string
12861      * @param {Mixed} s The value being converted
12862      * @return {String} The comparison value
12863      */
12864     asUCString : function(s) {
12865         return String(s).toUpperCase();
12866     },
12867     
12868     /**
12869      * Date sorting
12870      * @param {Mixed} s The value being converted
12871      * @return {Number} The comparison value
12872      */
12873     asDate : function(s) {
12874         if(!s){
12875             return 0;
12876         }
12877         if(s instanceof Date){
12878             return s.getTime();
12879         }
12880         return Date.parse(String(s));
12881     },
12882     
12883     /**
12884      * Float sorting
12885      * @param {Mixed} s The value being converted
12886      * @return {Float} The comparison value
12887      */
12888     asFloat : function(s) {
12889         var val = parseFloat(String(s).replace(/,/g, ""));
12890         if(isNaN(val)) {
12891             val = 0;
12892         }
12893         return val;
12894     },
12895     
12896     /**
12897      * Integer sorting
12898      * @param {Mixed} s The value being converted
12899      * @return {Number} The comparison value
12900      */
12901     asInt : function(s) {
12902         var val = parseInt(String(s).replace(/,/g, ""));
12903         if(isNaN(val)) {
12904             val = 0;
12905         }
12906         return val;
12907     }
12908 };/*
12909  * Based on:
12910  * Ext JS Library 1.1.1
12911  * Copyright(c) 2006-2007, Ext JS, LLC.
12912  *
12913  * Originally Released Under LGPL - original licence link has changed is not relivant.
12914  *
12915  * Fork - LGPL
12916  * <script type="text/javascript">
12917  */
12918
12919 /**
12920 * @class Roo.data.Record
12921  * Instances of this class encapsulate both record <em>definition</em> information, and record
12922  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12923  * to access Records cached in an {@link Roo.data.Store} object.<br>
12924  * <p>
12925  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12926  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12927  * objects.<br>
12928  * <p>
12929  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12930  * @constructor
12931  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12932  * {@link #create}. The parameters are the same.
12933  * @param {Array} data An associative Array of data values keyed by the field name.
12934  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12935  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12936  * not specified an integer id is generated.
12937  */
12938 Roo.data.Record = function(data, id){
12939     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12940     this.data = data;
12941 };
12942
12943 /**
12944  * Generate a constructor for a specific record layout.
12945  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12946  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12947  * Each field definition object may contain the following properties: <ul>
12948  * <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,
12949  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12950  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12951  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12952  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12953  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12954  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12955  * this may be omitted.</p></li>
12956  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12957  * <ul><li>auto (Default, implies no conversion)</li>
12958  * <li>string</li>
12959  * <li>int</li>
12960  * <li>float</li>
12961  * <li>boolean</li>
12962  * <li>date</li></ul></p></li>
12963  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12964  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12965  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12966  * by the Reader into an object that will be stored in the Record. It is passed the
12967  * following parameters:<ul>
12968  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12969  * </ul></p></li>
12970  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12971  * </ul>
12972  * <br>usage:<br><pre><code>
12973 var TopicRecord = Roo.data.Record.create(
12974     {name: 'title', mapping: 'topic_title'},
12975     {name: 'author', mapping: 'username'},
12976     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12977     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12978     {name: 'lastPoster', mapping: 'user2'},
12979     {name: 'excerpt', mapping: 'post_text'}
12980 );
12981
12982 var myNewRecord = new TopicRecord({
12983     title: 'Do my job please',
12984     author: 'noobie',
12985     totalPosts: 1,
12986     lastPost: new Date(),
12987     lastPoster: 'Animal',
12988     excerpt: 'No way dude!'
12989 });
12990 myStore.add(myNewRecord);
12991 </code></pre>
12992  * @method create
12993  * @static
12994  */
12995 Roo.data.Record.create = function(o){
12996     var f = function(){
12997         f.superclass.constructor.apply(this, arguments);
12998     };
12999     Roo.extend(f, Roo.data.Record);
13000     var p = f.prototype;
13001     p.fields = new Roo.util.MixedCollection(false, function(field){
13002         return field.name;
13003     });
13004     for(var i = 0, len = o.length; i < len; i++){
13005         p.fields.add(new Roo.data.Field(o[i]));
13006     }
13007     f.getField = function(name){
13008         return p.fields.get(name);  
13009     };
13010     return f;
13011 };
13012
13013 Roo.data.Record.AUTO_ID = 1000;
13014 Roo.data.Record.EDIT = 'edit';
13015 Roo.data.Record.REJECT = 'reject';
13016 Roo.data.Record.COMMIT = 'commit';
13017
13018 Roo.data.Record.prototype = {
13019     /**
13020      * Readonly flag - true if this record has been modified.
13021      * @type Boolean
13022      */
13023     dirty : false,
13024     editing : false,
13025     error: null,
13026     modified: null,
13027
13028     // private
13029     join : function(store){
13030         this.store = store;
13031     },
13032
13033     /**
13034      * Set the named field to the specified value.
13035      * @param {String} name The name of the field to set.
13036      * @param {Object} value The value to set the field to.
13037      */
13038     set : function(name, value){
13039         if(this.data[name] == value){
13040             return;
13041         }
13042         this.dirty = true;
13043         if(!this.modified){
13044             this.modified = {};
13045         }
13046         if(typeof this.modified[name] == 'undefined'){
13047             this.modified[name] = this.data[name];
13048         }
13049         this.data[name] = value;
13050         if(!this.editing && this.store){
13051             this.store.afterEdit(this);
13052         }       
13053     },
13054
13055     /**
13056      * Get the value of the named field.
13057      * @param {String} name The name of the field to get the value of.
13058      * @return {Object} The value of the field.
13059      */
13060     get : function(name){
13061         return this.data[name]; 
13062     },
13063
13064     // private
13065     beginEdit : function(){
13066         this.editing = true;
13067         this.modified = {}; 
13068     },
13069
13070     // private
13071     cancelEdit : function(){
13072         this.editing = false;
13073         delete this.modified;
13074     },
13075
13076     // private
13077     endEdit : function(){
13078         this.editing = false;
13079         if(this.dirty && this.store){
13080             this.store.afterEdit(this);
13081         }
13082     },
13083
13084     /**
13085      * Usually called by the {@link Roo.data.Store} which owns the Record.
13086      * Rejects all changes made to the Record since either creation, or the last commit operation.
13087      * Modified fields are reverted to their original values.
13088      * <p>
13089      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13090      * of reject operations.
13091      */
13092     reject : function(){
13093         var m = this.modified;
13094         for(var n in m){
13095             if(typeof m[n] != "function"){
13096                 this.data[n] = m[n];
13097             }
13098         }
13099         this.dirty = false;
13100         delete this.modified;
13101         this.editing = false;
13102         if(this.store){
13103             this.store.afterReject(this);
13104         }
13105     },
13106
13107     /**
13108      * Usually called by the {@link Roo.data.Store} which owns the Record.
13109      * Commits all changes made to the Record since either creation, or the last commit operation.
13110      * <p>
13111      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13112      * of commit operations.
13113      */
13114     commit : function(){
13115         this.dirty = false;
13116         delete this.modified;
13117         this.editing = false;
13118         if(this.store){
13119             this.store.afterCommit(this);
13120         }
13121     },
13122
13123     // private
13124     hasError : function(){
13125         return this.error != null;
13126     },
13127
13128     // private
13129     clearError : function(){
13130         this.error = null;
13131     },
13132
13133     /**
13134      * Creates a copy of this record.
13135      * @param {String} id (optional) A new record id if you don't want to use this record's id
13136      * @return {Record}
13137      */
13138     copy : function(newId) {
13139         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13140     }
13141 };/*
13142  * Based on:
13143  * Ext JS Library 1.1.1
13144  * Copyright(c) 2006-2007, Ext JS, LLC.
13145  *
13146  * Originally Released Under LGPL - original licence link has changed is not relivant.
13147  *
13148  * Fork - LGPL
13149  * <script type="text/javascript">
13150  */
13151
13152
13153
13154 /**
13155  * @class Roo.data.Store
13156  * @extends Roo.util.Observable
13157  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13158  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13159  * <p>
13160  * 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
13161  * has no knowledge of the format of the data returned by the Proxy.<br>
13162  * <p>
13163  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13164  * instances from the data object. These records are cached and made available through accessor functions.
13165  * @constructor
13166  * Creates a new Store.
13167  * @param {Object} config A config object containing the objects needed for the Store to access data,
13168  * and read the data into Records.
13169  */
13170 Roo.data.Store = function(config){
13171     this.data = new Roo.util.MixedCollection(false);
13172     this.data.getKey = function(o){
13173         return o.id;
13174     };
13175     this.baseParams = {};
13176     // private
13177     this.paramNames = {
13178         "start" : "start",
13179         "limit" : "limit",
13180         "sort" : "sort",
13181         "dir" : "dir",
13182         "multisort" : "_multisort"
13183     };
13184
13185     if(config && config.data){
13186         this.inlineData = config.data;
13187         delete config.data;
13188     }
13189
13190     Roo.apply(this, config);
13191     
13192     if(this.reader){ // reader passed
13193         this.reader = Roo.factory(this.reader, Roo.data);
13194         this.reader.xmodule = this.xmodule || false;
13195         if(!this.recordType){
13196             this.recordType = this.reader.recordType;
13197         }
13198         if(this.reader.onMetaChange){
13199             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13200         }
13201     }
13202
13203     if(this.recordType){
13204         this.fields = this.recordType.prototype.fields;
13205     }
13206     this.modified = [];
13207
13208     this.addEvents({
13209         /**
13210          * @event datachanged
13211          * Fires when the data cache has changed, and a widget which is using this Store
13212          * as a Record cache should refresh its view.
13213          * @param {Store} this
13214          */
13215         datachanged : true,
13216         /**
13217          * @event metachange
13218          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13219          * @param {Store} this
13220          * @param {Object} meta The JSON metadata
13221          */
13222         metachange : true,
13223         /**
13224          * @event add
13225          * Fires when Records have been added to the Store
13226          * @param {Store} this
13227          * @param {Roo.data.Record[]} records The array of Records added
13228          * @param {Number} index The index at which the record(s) were added
13229          */
13230         add : true,
13231         /**
13232          * @event remove
13233          * Fires when a Record has been removed from the Store
13234          * @param {Store} this
13235          * @param {Roo.data.Record} record The Record that was removed
13236          * @param {Number} index The index at which the record was removed
13237          */
13238         remove : true,
13239         /**
13240          * @event update
13241          * Fires when a Record has been updated
13242          * @param {Store} this
13243          * @param {Roo.data.Record} record The Record that was updated
13244          * @param {String} operation The update operation being performed.  Value may be one of:
13245          * <pre><code>
13246  Roo.data.Record.EDIT
13247  Roo.data.Record.REJECT
13248  Roo.data.Record.COMMIT
13249          * </code></pre>
13250          */
13251         update : true,
13252         /**
13253          * @event clear
13254          * Fires when the data cache has been cleared.
13255          * @param {Store} this
13256          */
13257         clear : true,
13258         /**
13259          * @event beforeload
13260          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13261          * the load action will be canceled.
13262          * @param {Store} this
13263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13264          */
13265         beforeload : true,
13266         /**
13267          * @event beforeloadadd
13268          * Fires after a new set of Records has been loaded.
13269          * @param {Store} this
13270          * @param {Roo.data.Record[]} records The Records that were loaded
13271          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13272          */
13273         beforeloadadd : true,
13274         /**
13275          * @event load
13276          * Fires after a new set of Records has been loaded, before they are added to the store.
13277          * @param {Store} this
13278          * @param {Roo.data.Record[]} records The Records that were loaded
13279          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13280          * @params {Object} return from reader
13281          */
13282         load : true,
13283         /**
13284          * @event loadexception
13285          * Fires if an exception occurs in the Proxy during loading.
13286          * Called with the signature of the Proxy's "loadexception" event.
13287          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13288          * 
13289          * @param {Proxy} 
13290          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13291          * @param {Object} load options 
13292          * @param {Object} jsonData from your request (normally this contains the Exception)
13293          */
13294         loadexception : true
13295     });
13296     
13297     if(this.proxy){
13298         this.proxy = Roo.factory(this.proxy, Roo.data);
13299         this.proxy.xmodule = this.xmodule || false;
13300         this.relayEvents(this.proxy,  ["loadexception"]);
13301     }
13302     this.sortToggle = {};
13303     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13304
13305     Roo.data.Store.superclass.constructor.call(this);
13306
13307     if(this.inlineData){
13308         this.loadData(this.inlineData);
13309         delete this.inlineData;
13310     }
13311 };
13312
13313 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13314      /**
13315     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13316     * without a remote query - used by combo/forms at present.
13317     */
13318     
13319     /**
13320     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13321     */
13322     /**
13323     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13324     */
13325     /**
13326     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13327     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13328     */
13329     /**
13330     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13331     * on any HTTP request
13332     */
13333     /**
13334     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13335     */
13336     /**
13337     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13338     */
13339     multiSort: false,
13340     /**
13341     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13342     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13343     */
13344     remoteSort : false,
13345
13346     /**
13347     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13348      * loaded or when a record is removed. (defaults to false).
13349     */
13350     pruneModifiedRecords : false,
13351
13352     // private
13353     lastOptions : null,
13354
13355     /**
13356      * Add Records to the Store and fires the add event.
13357      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13358      */
13359     add : function(records){
13360         records = [].concat(records);
13361         for(var i = 0, len = records.length; i < len; i++){
13362             records[i].join(this);
13363         }
13364         var index = this.data.length;
13365         this.data.addAll(records);
13366         this.fireEvent("add", this, records, index);
13367     },
13368
13369     /**
13370      * Remove a Record from the Store and fires the remove event.
13371      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13372      */
13373     remove : function(record){
13374         var index = this.data.indexOf(record);
13375         this.data.removeAt(index);
13376  
13377         if(this.pruneModifiedRecords){
13378             this.modified.remove(record);
13379         }
13380         this.fireEvent("remove", this, record, index);
13381     },
13382
13383     /**
13384      * Remove all Records from the Store and fires the clear event.
13385      */
13386     removeAll : function(){
13387         this.data.clear();
13388         if(this.pruneModifiedRecords){
13389             this.modified = [];
13390         }
13391         this.fireEvent("clear", this);
13392     },
13393
13394     /**
13395      * Inserts Records to the Store at the given index and fires the add event.
13396      * @param {Number} index The start index at which to insert the passed Records.
13397      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13398      */
13399     insert : function(index, records){
13400         records = [].concat(records);
13401         for(var i = 0, len = records.length; i < len; i++){
13402             this.data.insert(index, records[i]);
13403             records[i].join(this);
13404         }
13405         this.fireEvent("add", this, records, index);
13406     },
13407
13408     /**
13409      * Get the index within the cache of the passed Record.
13410      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13411      * @return {Number} The index of the passed Record. Returns -1 if not found.
13412      */
13413     indexOf : function(record){
13414         return this.data.indexOf(record);
13415     },
13416
13417     /**
13418      * Get the index within the cache of the Record with the passed id.
13419      * @param {String} id The id of the Record to find.
13420      * @return {Number} The index of the Record. Returns -1 if not found.
13421      */
13422     indexOfId : function(id){
13423         return this.data.indexOfKey(id);
13424     },
13425
13426     /**
13427      * Get the Record with the specified id.
13428      * @param {String} id The id of the Record to find.
13429      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13430      */
13431     getById : function(id){
13432         return this.data.key(id);
13433     },
13434
13435     /**
13436      * Get the Record at the specified index.
13437      * @param {Number} index The index of the Record to find.
13438      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13439      */
13440     getAt : function(index){
13441         return this.data.itemAt(index);
13442     },
13443
13444     /**
13445      * Returns a range of Records between specified indices.
13446      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13447      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13448      * @return {Roo.data.Record[]} An array of Records
13449      */
13450     getRange : function(start, end){
13451         return this.data.getRange(start, end);
13452     },
13453
13454     // private
13455     storeOptions : function(o){
13456         o = Roo.apply({}, o);
13457         delete o.callback;
13458         delete o.scope;
13459         this.lastOptions = o;
13460     },
13461
13462     /**
13463      * Loads the Record cache from the configured Proxy using the configured Reader.
13464      * <p>
13465      * If using remote paging, then the first load call must specify the <em>start</em>
13466      * and <em>limit</em> properties in the options.params property to establish the initial
13467      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13468      * <p>
13469      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13470      * and this call will return before the new data has been loaded. Perform any post-processing
13471      * in a callback function, or in a "load" event handler.</strong>
13472      * <p>
13473      * @param {Object} options An object containing properties which control loading options:<ul>
13474      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13475      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13476      * passed the following arguments:<ul>
13477      * <li>r : Roo.data.Record[]</li>
13478      * <li>options: Options object from the load call</li>
13479      * <li>success: Boolean success indicator</li></ul></li>
13480      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13481      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13482      * </ul>
13483      */
13484     load : function(options){
13485         options = options || {};
13486         if(this.fireEvent("beforeload", this, options) !== false){
13487             this.storeOptions(options);
13488             var p = Roo.apply(options.params || {}, this.baseParams);
13489             // if meta was not loaded from remote source.. try requesting it.
13490             if (!this.reader.metaFromRemote) {
13491                 p._requestMeta = 1;
13492             }
13493             if(this.sortInfo && this.remoteSort){
13494                 var pn = this.paramNames;
13495                 p[pn["sort"]] = this.sortInfo.field;
13496                 p[pn["dir"]] = this.sortInfo.direction;
13497             }
13498             if (this.multiSort) {
13499                 var pn = this.paramNames;
13500                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13501             }
13502             
13503             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13504         }
13505     },
13506
13507     /**
13508      * Reloads the Record cache from the configured Proxy using the configured Reader and
13509      * the options from the last load operation performed.
13510      * @param {Object} options (optional) An object containing properties which may override the options
13511      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13512      * the most recently used options are reused).
13513      */
13514     reload : function(options){
13515         this.load(Roo.applyIf(options||{}, this.lastOptions));
13516     },
13517
13518     // private
13519     // Called as a callback by the Reader during a load operation.
13520     loadRecords : function(o, options, success){
13521         if(!o || success === false){
13522             if(success !== false){
13523                 this.fireEvent("load", this, [], options, o);
13524             }
13525             if(options.callback){
13526                 options.callback.call(options.scope || this, [], options, false);
13527             }
13528             return;
13529         }
13530         // if data returned failure - throw an exception.
13531         if (o.success === false) {
13532             // show a message if no listener is registered.
13533             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13534                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13535             }
13536             // loadmask wil be hooked into this..
13537             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13538             return;
13539         }
13540         var r = o.records, t = o.totalRecords || r.length;
13541         
13542         this.fireEvent("beforeloadadd", this, r, options, o);
13543         
13544         if(!options || options.add !== true){
13545             if(this.pruneModifiedRecords){
13546                 this.modified = [];
13547             }
13548             for(var i = 0, len = r.length; i < len; i++){
13549                 r[i].join(this);
13550             }
13551             if(this.snapshot){
13552                 this.data = this.snapshot;
13553                 delete this.snapshot;
13554             }
13555             this.data.clear();
13556             this.data.addAll(r);
13557             this.totalLength = t;
13558             this.applySort();
13559             this.fireEvent("datachanged", this);
13560         }else{
13561             this.totalLength = Math.max(t, this.data.length+r.length);
13562             this.add(r);
13563         }
13564         
13565         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13566                 
13567             var e = new Roo.data.Record({});
13568
13569             e.set(this.parent.displayField, this.parent.emptyTitle);
13570             e.set(this.parent.valueField, '');
13571
13572             this.insert(0, e);
13573         }
13574             
13575         this.fireEvent("load", this, r, options, o);
13576         if(options.callback){
13577             options.callback.call(options.scope || this, r, options, true);
13578         }
13579     },
13580
13581
13582     /**
13583      * Loads data from a passed data block. A Reader which understands the format of the data
13584      * must have been configured in the constructor.
13585      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13586      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13587      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13588      */
13589     loadData : function(o, append){
13590         var r = this.reader.readRecords(o);
13591         this.loadRecords(r, {add: append}, true);
13592     },
13593     
13594      /**
13595      * using 'cn' the nested child reader read the child array into it's child stores.
13596      * @param {Object} rec The record with a 'children array
13597      */
13598     loadDataFromChildren : function(rec)
13599     {
13600         this.loadData(this.reader.toLoadData(rec));
13601     },
13602     
13603
13604     /**
13605      * Gets the number of cached records.
13606      * <p>
13607      * <em>If using paging, this may not be the total size of the dataset. If the data object
13608      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13609      * the data set size</em>
13610      */
13611     getCount : function(){
13612         return this.data.length || 0;
13613     },
13614
13615     /**
13616      * Gets the total number of records in the dataset as returned by the server.
13617      * <p>
13618      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13619      * the dataset size</em>
13620      */
13621     getTotalCount : function(){
13622         return this.totalLength || 0;
13623     },
13624
13625     /**
13626      * Returns the sort state of the Store as an object with two properties:
13627      * <pre><code>
13628  field {String} The name of the field by which the Records are sorted
13629  direction {String} The sort order, "ASC" or "DESC"
13630      * </code></pre>
13631      */
13632     getSortState : function(){
13633         return this.sortInfo;
13634     },
13635
13636     // private
13637     applySort : function(){
13638         if(this.sortInfo && !this.remoteSort){
13639             var s = this.sortInfo, f = s.field;
13640             var st = this.fields.get(f).sortType;
13641             var fn = function(r1, r2){
13642                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13643                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13644             };
13645             this.data.sort(s.direction, fn);
13646             if(this.snapshot && this.snapshot != this.data){
13647                 this.snapshot.sort(s.direction, fn);
13648             }
13649         }
13650     },
13651
13652     /**
13653      * Sets the default sort column and order to be used by the next load operation.
13654      * @param {String} fieldName The name of the field to sort by.
13655      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13656      */
13657     setDefaultSort : function(field, dir){
13658         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13659     },
13660
13661     /**
13662      * Sort the Records.
13663      * If remote sorting is used, the sort is performed on the server, and the cache is
13664      * reloaded. If local sorting is used, the cache is sorted internally.
13665      * @param {String} fieldName The name of the field to sort by.
13666      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13667      */
13668     sort : function(fieldName, dir){
13669         var f = this.fields.get(fieldName);
13670         if(!dir){
13671             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13672             
13673             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13674                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13675             }else{
13676                 dir = f.sortDir;
13677             }
13678         }
13679         this.sortToggle[f.name] = dir;
13680         this.sortInfo = {field: f.name, direction: dir};
13681         if(!this.remoteSort){
13682             this.applySort();
13683             this.fireEvent("datachanged", this);
13684         }else{
13685             this.load(this.lastOptions);
13686         }
13687     },
13688
13689     /**
13690      * Calls the specified function for each of the Records in the cache.
13691      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13692      * Returning <em>false</em> aborts and exits the iteration.
13693      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13694      */
13695     each : function(fn, scope){
13696         this.data.each(fn, scope);
13697     },
13698
13699     /**
13700      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13701      * (e.g., during paging).
13702      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13703      */
13704     getModifiedRecords : function(){
13705         return this.modified;
13706     },
13707
13708     // private
13709     createFilterFn : function(property, value, anyMatch){
13710         if(!value.exec){ // not a regex
13711             value = String(value);
13712             if(value.length == 0){
13713                 return false;
13714             }
13715             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13716         }
13717         return function(r){
13718             return value.test(r.data[property]);
13719         };
13720     },
13721
13722     /**
13723      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13724      * @param {String} property A field on your records
13725      * @param {Number} start The record index to start at (defaults to 0)
13726      * @param {Number} end The last record index to include (defaults to length - 1)
13727      * @return {Number} The sum
13728      */
13729     sum : function(property, start, end){
13730         var rs = this.data.items, v = 0;
13731         start = start || 0;
13732         end = (end || end === 0) ? end : rs.length-1;
13733
13734         for(var i = start; i <= end; i++){
13735             v += (rs[i].data[property] || 0);
13736         }
13737         return v;
13738     },
13739
13740     /**
13741      * Filter the records by a specified property.
13742      * @param {String} field A field on your records
13743      * @param {String/RegExp} value Either a string that the field
13744      * should start with or a RegExp to test against the field
13745      * @param {Boolean} anyMatch True to match any part not just the beginning
13746      */
13747     filter : function(property, value, anyMatch){
13748         var fn = this.createFilterFn(property, value, anyMatch);
13749         return fn ? this.filterBy(fn) : this.clearFilter();
13750     },
13751
13752     /**
13753      * Filter by a function. The specified function will be called with each
13754      * record in this data source. If the function returns true the record is included,
13755      * otherwise it is filtered.
13756      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13757      * @param {Object} scope (optional) The scope of the function (defaults to this)
13758      */
13759     filterBy : function(fn, scope){
13760         this.snapshot = this.snapshot || this.data;
13761         this.data = this.queryBy(fn, scope||this);
13762         this.fireEvent("datachanged", this);
13763     },
13764
13765     /**
13766      * Query the records by a specified property.
13767      * @param {String} field A field on your records
13768      * @param {String/RegExp} value Either a string that the field
13769      * should start with or a RegExp to test against the field
13770      * @param {Boolean} anyMatch True to match any part not just the beginning
13771      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13772      */
13773     query : function(property, value, anyMatch){
13774         var fn = this.createFilterFn(property, value, anyMatch);
13775         return fn ? this.queryBy(fn) : this.data.clone();
13776     },
13777
13778     /**
13779      * Query by a function. The specified function will be called with each
13780      * record in this data source. If the function returns true the record is included
13781      * in the results.
13782      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13783      * @param {Object} scope (optional) The scope of the function (defaults to this)
13784       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13785      **/
13786     queryBy : function(fn, scope){
13787         var data = this.snapshot || this.data;
13788         return data.filterBy(fn, scope||this);
13789     },
13790
13791     /**
13792      * Collects unique values for a particular dataIndex from this store.
13793      * @param {String} dataIndex The property to collect
13794      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13795      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13796      * @return {Array} An array of the unique values
13797      **/
13798     collect : function(dataIndex, allowNull, bypassFilter){
13799         var d = (bypassFilter === true && this.snapshot) ?
13800                 this.snapshot.items : this.data.items;
13801         var v, sv, r = [], l = {};
13802         for(var i = 0, len = d.length; i < len; i++){
13803             v = d[i].data[dataIndex];
13804             sv = String(v);
13805             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13806                 l[sv] = true;
13807                 r[r.length] = v;
13808             }
13809         }
13810         return r;
13811     },
13812
13813     /**
13814      * Revert to a view of the Record cache with no filtering applied.
13815      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13816      */
13817     clearFilter : function(suppressEvent){
13818         if(this.snapshot && this.snapshot != this.data){
13819             this.data = this.snapshot;
13820             delete this.snapshot;
13821             if(suppressEvent !== true){
13822                 this.fireEvent("datachanged", this);
13823             }
13824         }
13825     },
13826
13827     // private
13828     afterEdit : function(record){
13829         if(this.modified.indexOf(record) == -1){
13830             this.modified.push(record);
13831         }
13832         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13833     },
13834     
13835     // private
13836     afterReject : function(record){
13837         this.modified.remove(record);
13838         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13839     },
13840
13841     // private
13842     afterCommit : function(record){
13843         this.modified.remove(record);
13844         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13845     },
13846
13847     /**
13848      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13849      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13850      */
13851     commitChanges : function(){
13852         var m = this.modified.slice(0);
13853         this.modified = [];
13854         for(var i = 0, len = m.length; i < len; i++){
13855             m[i].commit();
13856         }
13857     },
13858
13859     /**
13860      * Cancel outstanding changes on all changed records.
13861      */
13862     rejectChanges : function(){
13863         var m = this.modified.slice(0);
13864         this.modified = [];
13865         for(var i = 0, len = m.length; i < len; i++){
13866             m[i].reject();
13867         }
13868     },
13869
13870     onMetaChange : function(meta, rtype, o){
13871         this.recordType = rtype;
13872         this.fields = rtype.prototype.fields;
13873         delete this.snapshot;
13874         this.sortInfo = meta.sortInfo || this.sortInfo;
13875         this.modified = [];
13876         this.fireEvent('metachange', this, this.reader.meta);
13877     },
13878     
13879     moveIndex : function(data, type)
13880     {
13881         var index = this.indexOf(data);
13882         
13883         var newIndex = index + type;
13884         
13885         this.remove(data);
13886         
13887         this.insert(newIndex, data);
13888         
13889     }
13890 });/*
13891  * Based on:
13892  * Ext JS Library 1.1.1
13893  * Copyright(c) 2006-2007, Ext JS, LLC.
13894  *
13895  * Originally Released Under LGPL - original licence link has changed is not relivant.
13896  *
13897  * Fork - LGPL
13898  * <script type="text/javascript">
13899  */
13900
13901 /**
13902  * @class Roo.data.SimpleStore
13903  * @extends Roo.data.Store
13904  * Small helper class to make creating Stores from Array data easier.
13905  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13906  * @cfg {Array} fields An array of field definition objects, or field name strings.
13907  * @cfg {Object} an existing reader (eg. copied from another store)
13908  * @cfg {Array} data The multi-dimensional array of data
13909  * @constructor
13910  * @param {Object} config
13911  */
13912 Roo.data.SimpleStore = function(config)
13913 {
13914     Roo.data.SimpleStore.superclass.constructor.call(this, {
13915         isLocal : true,
13916         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13917                 id: config.id
13918             },
13919             Roo.data.Record.create(config.fields)
13920         ),
13921         proxy : new Roo.data.MemoryProxy(config.data)
13922     });
13923     this.load();
13924 };
13925 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13926  * Based on:
13927  * Ext JS Library 1.1.1
13928  * Copyright(c) 2006-2007, Ext JS, LLC.
13929  *
13930  * Originally Released Under LGPL - original licence link has changed is not relivant.
13931  *
13932  * Fork - LGPL
13933  * <script type="text/javascript">
13934  */
13935
13936 /**
13937 /**
13938  * @extends Roo.data.Store
13939  * @class Roo.data.JsonStore
13940  * Small helper class to make creating Stores for JSON data easier. <br/>
13941 <pre><code>
13942 var store = new Roo.data.JsonStore({
13943     url: 'get-images.php',
13944     root: 'images',
13945     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13946 });
13947 </code></pre>
13948  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13949  * JsonReader and HttpProxy (unless inline data is provided).</b>
13950  * @cfg {Array} fields An array of field definition objects, or field name strings.
13951  * @constructor
13952  * @param {Object} config
13953  */
13954 Roo.data.JsonStore = function(c){
13955     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13956         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13957         reader: new Roo.data.JsonReader(c, c.fields)
13958     }));
13959 };
13960 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13961  * Based on:
13962  * Ext JS Library 1.1.1
13963  * Copyright(c) 2006-2007, Ext JS, LLC.
13964  *
13965  * Originally Released Under LGPL - original licence link has changed is not relivant.
13966  *
13967  * Fork - LGPL
13968  * <script type="text/javascript">
13969  */
13970
13971  
13972 Roo.data.Field = function(config){
13973     if(typeof config == "string"){
13974         config = {name: config};
13975     }
13976     Roo.apply(this, config);
13977     
13978     if(!this.type){
13979         this.type = "auto";
13980     }
13981     
13982     var st = Roo.data.SortTypes;
13983     // named sortTypes are supported, here we look them up
13984     if(typeof this.sortType == "string"){
13985         this.sortType = st[this.sortType];
13986     }
13987     
13988     // set default sortType for strings and dates
13989     if(!this.sortType){
13990         switch(this.type){
13991             case "string":
13992                 this.sortType = st.asUCString;
13993                 break;
13994             case "date":
13995                 this.sortType = st.asDate;
13996                 break;
13997             default:
13998                 this.sortType = st.none;
13999         }
14000     }
14001
14002     // define once
14003     var stripRe = /[\$,%]/g;
14004
14005     // prebuilt conversion function for this field, instead of
14006     // switching every time we're reading a value
14007     if(!this.convert){
14008         var cv, dateFormat = this.dateFormat;
14009         switch(this.type){
14010             case "":
14011             case "auto":
14012             case undefined:
14013                 cv = function(v){ return v; };
14014                 break;
14015             case "string":
14016                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14017                 break;
14018             case "int":
14019                 cv = function(v){
14020                     return v !== undefined && v !== null && v !== '' ?
14021                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14022                     };
14023                 break;
14024             case "float":
14025                 cv = function(v){
14026                     return v !== undefined && v !== null && v !== '' ?
14027                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14028                     };
14029                 break;
14030             case "bool":
14031             case "boolean":
14032                 cv = function(v){ return v === true || v === "true" || v == 1; };
14033                 break;
14034             case "date":
14035                 cv = function(v){
14036                     if(!v){
14037                         return '';
14038                     }
14039                     if(v instanceof Date){
14040                         return v;
14041                     }
14042                     if(dateFormat){
14043                         if(dateFormat == "timestamp"){
14044                             return new Date(v*1000);
14045                         }
14046                         return Date.parseDate(v, dateFormat);
14047                     }
14048                     var parsed = Date.parse(v);
14049                     return parsed ? new Date(parsed) : null;
14050                 };
14051              break;
14052             
14053         }
14054         this.convert = cv;
14055     }
14056 };
14057
14058 Roo.data.Field.prototype = {
14059     dateFormat: null,
14060     defaultValue: "",
14061     mapping: null,
14062     sortType : null,
14063     sortDir : "ASC"
14064 };/*
14065  * Based on:
14066  * Ext JS Library 1.1.1
14067  * Copyright(c) 2006-2007, Ext JS, LLC.
14068  *
14069  * Originally Released Under LGPL - original licence link has changed is not relivant.
14070  *
14071  * Fork - LGPL
14072  * <script type="text/javascript">
14073  */
14074  
14075 // Base class for reading structured data from a data source.  This class is intended to be
14076 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14077
14078 /**
14079  * @class Roo.data.DataReader
14080  * Base class for reading structured data from a data source.  This class is intended to be
14081  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14082  */
14083
14084 Roo.data.DataReader = function(meta, recordType){
14085     
14086     this.meta = meta;
14087     
14088     this.recordType = recordType instanceof Array ? 
14089         Roo.data.Record.create(recordType) : recordType;
14090 };
14091
14092 Roo.data.DataReader.prototype = {
14093     
14094     
14095     readerType : 'Data',
14096      /**
14097      * Create an empty record
14098      * @param {Object} data (optional) - overlay some values
14099      * @return {Roo.data.Record} record created.
14100      */
14101     newRow :  function(d) {
14102         var da =  {};
14103         this.recordType.prototype.fields.each(function(c) {
14104             switch( c.type) {
14105                 case 'int' : da[c.name] = 0; break;
14106                 case 'date' : da[c.name] = new Date(); break;
14107                 case 'float' : da[c.name] = 0.0; break;
14108                 case 'boolean' : da[c.name] = false; break;
14109                 default : da[c.name] = ""; break;
14110             }
14111             
14112         });
14113         return new this.recordType(Roo.apply(da, d));
14114     }
14115     
14116     
14117 };/*
14118  * Based on:
14119  * Ext JS Library 1.1.1
14120  * Copyright(c) 2006-2007, Ext JS, LLC.
14121  *
14122  * Originally Released Under LGPL - original licence link has changed is not relivant.
14123  *
14124  * Fork - LGPL
14125  * <script type="text/javascript">
14126  */
14127
14128 /**
14129  * @class Roo.data.DataProxy
14130  * @extends Roo.data.Observable
14131  * This class is an abstract base class for implementations which provide retrieval of
14132  * unformatted data objects.<br>
14133  * <p>
14134  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14135  * (of the appropriate type which knows how to parse the data object) to provide a block of
14136  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14137  * <p>
14138  * Custom implementations must implement the load method as described in
14139  * {@link Roo.data.HttpProxy#load}.
14140  */
14141 Roo.data.DataProxy = function(){
14142     this.addEvents({
14143         /**
14144          * @event beforeload
14145          * Fires before a network request is made to retrieve a data object.
14146          * @param {Object} This DataProxy object.
14147          * @param {Object} params The params parameter to the load function.
14148          */
14149         beforeload : true,
14150         /**
14151          * @event load
14152          * Fires before the load method's callback is called.
14153          * @param {Object} This DataProxy object.
14154          * @param {Object} o The data object.
14155          * @param {Object} arg The callback argument object passed to the load function.
14156          */
14157         load : true,
14158         /**
14159          * @event loadexception
14160          * Fires if an Exception occurs during data retrieval.
14161          * @param {Object} This DataProxy object.
14162          * @param {Object} o The data object.
14163          * @param {Object} arg The callback argument object passed to the load function.
14164          * @param {Object} e The Exception.
14165          */
14166         loadexception : true
14167     });
14168     Roo.data.DataProxy.superclass.constructor.call(this);
14169 };
14170
14171 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14172
14173     /**
14174      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14175      */
14176 /*
14177  * Based on:
14178  * Ext JS Library 1.1.1
14179  * Copyright(c) 2006-2007, Ext JS, LLC.
14180  *
14181  * Originally Released Under LGPL - original licence link has changed is not relivant.
14182  *
14183  * Fork - LGPL
14184  * <script type="text/javascript">
14185  */
14186 /**
14187  * @class Roo.data.MemoryProxy
14188  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14189  * to the Reader when its load method is called.
14190  * @constructor
14191  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14192  */
14193 Roo.data.MemoryProxy = function(data){
14194     if (data.data) {
14195         data = data.data;
14196     }
14197     Roo.data.MemoryProxy.superclass.constructor.call(this);
14198     this.data = data;
14199 };
14200
14201 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14202     
14203     /**
14204      * Load data from the requested source (in this case an in-memory
14205      * data object passed to the constructor), read the data object into
14206      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14207      * process that block using the passed callback.
14208      * @param {Object} params This parameter is not used by the MemoryProxy class.
14209      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14210      * object into a block of Roo.data.Records.
14211      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14212      * The function must be passed <ul>
14213      * <li>The Record block object</li>
14214      * <li>The "arg" argument from the load function</li>
14215      * <li>A boolean success indicator</li>
14216      * </ul>
14217      * @param {Object} scope The scope in which to call the callback
14218      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14219      */
14220     load : function(params, reader, callback, scope, arg){
14221         params = params || {};
14222         var result;
14223         try {
14224             result = reader.readRecords(params.data ? params.data :this.data);
14225         }catch(e){
14226             this.fireEvent("loadexception", this, arg, null, e);
14227             callback.call(scope, null, arg, false);
14228             return;
14229         }
14230         callback.call(scope, result, arg, true);
14231     },
14232     
14233     // private
14234     update : function(params, records){
14235         
14236     }
14237 });/*
14238  * Based on:
14239  * Ext JS Library 1.1.1
14240  * Copyright(c) 2006-2007, Ext JS, LLC.
14241  *
14242  * Originally Released Under LGPL - original licence link has changed is not relivant.
14243  *
14244  * Fork - LGPL
14245  * <script type="text/javascript">
14246  */
14247 /**
14248  * @class Roo.data.HttpProxy
14249  * @extends Roo.data.DataProxy
14250  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14251  * configured to reference a certain URL.<br><br>
14252  * <p>
14253  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14254  * from which the running page was served.<br><br>
14255  * <p>
14256  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14257  * <p>
14258  * Be aware that to enable the browser to parse an XML document, the server must set
14259  * the Content-Type header in the HTTP response to "text/xml".
14260  * @constructor
14261  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14262  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14263  * will be used to make the request.
14264  */
14265 Roo.data.HttpProxy = function(conn){
14266     Roo.data.HttpProxy.superclass.constructor.call(this);
14267     // is conn a conn config or a real conn?
14268     this.conn = conn;
14269     this.useAjax = !conn || !conn.events;
14270   
14271 };
14272
14273 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14274     // thse are take from connection...
14275     
14276     /**
14277      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14278      */
14279     /**
14280      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14281      * extra parameters to each request made by this object. (defaults to undefined)
14282      */
14283     /**
14284      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14285      *  to each request made by this object. (defaults to undefined)
14286      */
14287     /**
14288      * @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)
14289      */
14290     /**
14291      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14292      */
14293      /**
14294      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14295      * @type Boolean
14296      */
14297   
14298
14299     /**
14300      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14301      * @type Boolean
14302      */
14303     /**
14304      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14305      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14306      * a finer-grained basis than the DataProxy events.
14307      */
14308     getConnection : function(){
14309         return this.useAjax ? Roo.Ajax : this.conn;
14310     },
14311
14312     /**
14313      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14314      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14315      * process that block using the passed callback.
14316      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14317      * for the request to the remote server.
14318      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14319      * object into a block of Roo.data.Records.
14320      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14321      * The function must be passed <ul>
14322      * <li>The Record block object</li>
14323      * <li>The "arg" argument from the load function</li>
14324      * <li>A boolean success indicator</li>
14325      * </ul>
14326      * @param {Object} scope The scope in which to call the callback
14327      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14328      */
14329     load : function(params, reader, callback, scope, arg){
14330         if(this.fireEvent("beforeload", this, params) !== false){
14331             var  o = {
14332                 params : params || {},
14333                 request: {
14334                     callback : callback,
14335                     scope : scope,
14336                     arg : arg
14337                 },
14338                 reader: reader,
14339                 callback : this.loadResponse,
14340                 scope: this
14341             };
14342             if(this.useAjax){
14343                 Roo.applyIf(o, this.conn);
14344                 if(this.activeRequest){
14345                     Roo.Ajax.abort(this.activeRequest);
14346                 }
14347                 this.activeRequest = Roo.Ajax.request(o);
14348             }else{
14349                 this.conn.request(o);
14350             }
14351         }else{
14352             callback.call(scope||this, null, arg, false);
14353         }
14354     },
14355
14356     // private
14357     loadResponse : function(o, success, response){
14358         delete this.activeRequest;
14359         if(!success){
14360             this.fireEvent("loadexception", this, o, response);
14361             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14362             return;
14363         }
14364         var result;
14365         try {
14366             result = o.reader.read(response);
14367         }catch(e){
14368             this.fireEvent("loadexception", this, o, response, e);
14369             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14370             return;
14371         }
14372         
14373         this.fireEvent("load", this, o, o.request.arg);
14374         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14375     },
14376
14377     // private
14378     update : function(dataSet){
14379
14380     },
14381
14382     // private
14383     updateResponse : function(dataSet){
14384
14385     }
14386 });/*
14387  * Based on:
14388  * Ext JS Library 1.1.1
14389  * Copyright(c) 2006-2007, Ext JS, LLC.
14390  *
14391  * Originally Released Under LGPL - original licence link has changed is not relivant.
14392  *
14393  * Fork - LGPL
14394  * <script type="text/javascript">
14395  */
14396
14397 /**
14398  * @class Roo.data.ScriptTagProxy
14399  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14400  * other than the originating domain of the running page.<br><br>
14401  * <p>
14402  * <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
14403  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14404  * <p>
14405  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14406  * source code that is used as the source inside a &lt;script> tag.<br><br>
14407  * <p>
14408  * In order for the browser to process the returned data, the server must wrap the data object
14409  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14410  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14411  * depending on whether the callback name was passed:
14412  * <p>
14413  * <pre><code>
14414 boolean scriptTag = false;
14415 String cb = request.getParameter("callback");
14416 if (cb != null) {
14417     scriptTag = true;
14418     response.setContentType("text/javascript");
14419 } else {
14420     response.setContentType("application/x-json");
14421 }
14422 Writer out = response.getWriter();
14423 if (scriptTag) {
14424     out.write(cb + "(");
14425 }
14426 out.print(dataBlock.toJsonString());
14427 if (scriptTag) {
14428     out.write(");");
14429 }
14430 </pre></code>
14431  *
14432  * @constructor
14433  * @param {Object} config A configuration object.
14434  */
14435 Roo.data.ScriptTagProxy = function(config){
14436     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14437     Roo.apply(this, config);
14438     this.head = document.getElementsByTagName("head")[0];
14439 };
14440
14441 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14442
14443 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14444     /**
14445      * @cfg {String} url The URL from which to request the data object.
14446      */
14447     /**
14448      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14449      */
14450     timeout : 30000,
14451     /**
14452      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14453      * the server the name of the callback function set up by the load call to process the returned data object.
14454      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14455      * javascript output which calls this named function passing the data object as its only parameter.
14456      */
14457     callbackParam : "callback",
14458     /**
14459      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14460      * name to the request.
14461      */
14462     nocache : true,
14463
14464     /**
14465      * Load data from the configured URL, read the data object into
14466      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14467      * process that block using the passed callback.
14468      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14469      * for the request to the remote server.
14470      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14471      * object into a block of Roo.data.Records.
14472      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14473      * The function must be passed <ul>
14474      * <li>The Record block object</li>
14475      * <li>The "arg" argument from the load function</li>
14476      * <li>A boolean success indicator</li>
14477      * </ul>
14478      * @param {Object} scope The scope in which to call the callback
14479      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14480      */
14481     load : function(params, reader, callback, scope, arg){
14482         if(this.fireEvent("beforeload", this, params) !== false){
14483
14484             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14485
14486             var url = this.url;
14487             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14488             if(this.nocache){
14489                 url += "&_dc=" + (new Date().getTime());
14490             }
14491             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14492             var trans = {
14493                 id : transId,
14494                 cb : "stcCallback"+transId,
14495                 scriptId : "stcScript"+transId,
14496                 params : params,
14497                 arg : arg,
14498                 url : url,
14499                 callback : callback,
14500                 scope : scope,
14501                 reader : reader
14502             };
14503             var conn = this;
14504
14505             window[trans.cb] = function(o){
14506                 conn.handleResponse(o, trans);
14507             };
14508
14509             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14510
14511             if(this.autoAbort !== false){
14512                 this.abort();
14513             }
14514
14515             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14516
14517             var script = document.createElement("script");
14518             script.setAttribute("src", url);
14519             script.setAttribute("type", "text/javascript");
14520             script.setAttribute("id", trans.scriptId);
14521             this.head.appendChild(script);
14522
14523             this.trans = trans;
14524         }else{
14525             callback.call(scope||this, null, arg, false);
14526         }
14527     },
14528
14529     // private
14530     isLoading : function(){
14531         return this.trans ? true : false;
14532     },
14533
14534     /**
14535      * Abort the current server request.
14536      */
14537     abort : function(){
14538         if(this.isLoading()){
14539             this.destroyTrans(this.trans);
14540         }
14541     },
14542
14543     // private
14544     destroyTrans : function(trans, isLoaded){
14545         this.head.removeChild(document.getElementById(trans.scriptId));
14546         clearTimeout(trans.timeoutId);
14547         if(isLoaded){
14548             window[trans.cb] = undefined;
14549             try{
14550                 delete window[trans.cb];
14551             }catch(e){}
14552         }else{
14553             // if hasn't been loaded, wait for load to remove it to prevent script error
14554             window[trans.cb] = function(){
14555                 window[trans.cb] = undefined;
14556                 try{
14557                     delete window[trans.cb];
14558                 }catch(e){}
14559             };
14560         }
14561     },
14562
14563     // private
14564     handleResponse : function(o, trans){
14565         this.trans = false;
14566         this.destroyTrans(trans, true);
14567         var result;
14568         try {
14569             result = trans.reader.readRecords(o);
14570         }catch(e){
14571             this.fireEvent("loadexception", this, o, trans.arg, e);
14572             trans.callback.call(trans.scope||window, null, trans.arg, false);
14573             return;
14574         }
14575         this.fireEvent("load", this, o, trans.arg);
14576         trans.callback.call(trans.scope||window, result, trans.arg, true);
14577     },
14578
14579     // private
14580     handleFailure : function(trans){
14581         this.trans = false;
14582         this.destroyTrans(trans, false);
14583         this.fireEvent("loadexception", this, null, trans.arg);
14584         trans.callback.call(trans.scope||window, null, trans.arg, false);
14585     }
14586 });/*
14587  * Based on:
14588  * Ext JS Library 1.1.1
14589  * Copyright(c) 2006-2007, Ext JS, LLC.
14590  *
14591  * Originally Released Under LGPL - original licence link has changed is not relivant.
14592  *
14593  * Fork - LGPL
14594  * <script type="text/javascript">
14595  */
14596
14597 /**
14598  * @class Roo.data.JsonReader
14599  * @extends Roo.data.DataReader
14600  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14601  * based on mappings in a provided Roo.data.Record constructor.
14602  * 
14603  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14604  * in the reply previously. 
14605  * 
14606  * <p>
14607  * Example code:
14608  * <pre><code>
14609 var RecordDef = Roo.data.Record.create([
14610     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14611     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14612 ]);
14613 var myReader = new Roo.data.JsonReader({
14614     totalProperty: "results",    // The property which contains the total dataset size (optional)
14615     root: "rows",                // The property which contains an Array of row objects
14616     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14617 }, RecordDef);
14618 </code></pre>
14619  * <p>
14620  * This would consume a JSON file like this:
14621  * <pre><code>
14622 { 'results': 2, 'rows': [
14623     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14624     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14625 }
14626 </code></pre>
14627  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14628  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14629  * paged from the remote server.
14630  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14631  * @cfg {String} root name of the property which contains the Array of row objects.
14632  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14633  * @cfg {Array} fields Array of field definition objects
14634  * @constructor
14635  * Create a new JsonReader
14636  * @param {Object} meta Metadata configuration options
14637  * @param {Object} recordType Either an Array of field definition objects,
14638  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14639  */
14640 Roo.data.JsonReader = function(meta, recordType){
14641     
14642     meta = meta || {};
14643     // set some defaults:
14644     Roo.applyIf(meta, {
14645         totalProperty: 'total',
14646         successProperty : 'success',
14647         root : 'data',
14648         id : 'id'
14649     });
14650     
14651     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14652 };
14653 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14654     
14655     readerType : 'Json',
14656     
14657     /**
14658      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14659      * Used by Store query builder to append _requestMeta to params.
14660      * 
14661      */
14662     metaFromRemote : false,
14663     /**
14664      * This method is only used by a DataProxy which has retrieved data from a remote server.
14665      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14666      * @return {Object} data A data block which is used by an Roo.data.Store object as
14667      * a cache of Roo.data.Records.
14668      */
14669     read : function(response){
14670         var json = response.responseText;
14671        
14672         var o = /* eval:var:o */ eval("("+json+")");
14673         if(!o) {
14674             throw {message: "JsonReader.read: Json object not found"};
14675         }
14676         
14677         if(o.metaData){
14678             
14679             delete this.ef;
14680             this.metaFromRemote = true;
14681             this.meta = o.metaData;
14682             this.recordType = Roo.data.Record.create(o.metaData.fields);
14683             this.onMetaChange(this.meta, this.recordType, o);
14684         }
14685         return this.readRecords(o);
14686     },
14687
14688     // private function a store will implement
14689     onMetaChange : function(meta, recordType, o){
14690
14691     },
14692
14693     /**
14694          * @ignore
14695          */
14696     simpleAccess: function(obj, subsc) {
14697         return obj[subsc];
14698     },
14699
14700         /**
14701          * @ignore
14702          */
14703     getJsonAccessor: function(){
14704         var re = /[\[\.]/;
14705         return function(expr) {
14706             try {
14707                 return(re.test(expr))
14708                     ? new Function("obj", "return obj." + expr)
14709                     : function(obj){
14710                         return obj[expr];
14711                     };
14712             } catch(e){}
14713             return Roo.emptyFn;
14714         };
14715     }(),
14716
14717     /**
14718      * Create a data block containing Roo.data.Records from an XML document.
14719      * @param {Object} o An object which contains an Array of row objects in the property specified
14720      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14721      * which contains the total size of the dataset.
14722      * @return {Object} data A data block which is used by an Roo.data.Store object as
14723      * a cache of Roo.data.Records.
14724      */
14725     readRecords : function(o){
14726         /**
14727          * After any data loads, the raw JSON data is available for further custom processing.
14728          * @type Object
14729          */
14730         this.o = o;
14731         var s = this.meta, Record = this.recordType,
14732             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14733
14734 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14735         if (!this.ef) {
14736             if(s.totalProperty) {
14737                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14738                 }
14739                 if(s.successProperty) {
14740                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14741                 }
14742                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14743                 if (s.id) {
14744                         var g = this.getJsonAccessor(s.id);
14745                         this.getId = function(rec) {
14746                                 var r = g(rec);  
14747                                 return (r === undefined || r === "") ? null : r;
14748                         };
14749                 } else {
14750                         this.getId = function(){return null;};
14751                 }
14752             this.ef = [];
14753             for(var jj = 0; jj < fl; jj++){
14754                 f = fi[jj];
14755                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14756                 this.ef[jj] = this.getJsonAccessor(map);
14757             }
14758         }
14759
14760         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14761         if(s.totalProperty){
14762             var vt = parseInt(this.getTotal(o), 10);
14763             if(!isNaN(vt)){
14764                 totalRecords = vt;
14765             }
14766         }
14767         if(s.successProperty){
14768             var vs = this.getSuccess(o);
14769             if(vs === false || vs === 'false'){
14770                 success = false;
14771             }
14772         }
14773         var records = [];
14774         for(var i = 0; i < c; i++){
14775                 var n = root[i];
14776             var values = {};
14777             var id = this.getId(n);
14778             for(var j = 0; j < fl; j++){
14779                 f = fi[j];
14780             var v = this.ef[j](n);
14781             if (!f.convert) {
14782                 Roo.log('missing convert for ' + f.name);
14783                 Roo.log(f);
14784                 continue;
14785             }
14786             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14787             }
14788             var record = new Record(values, id);
14789             record.json = n;
14790             records[i] = record;
14791         }
14792         return {
14793             raw : o,
14794             success : success,
14795             records : records,
14796             totalRecords : totalRecords
14797         };
14798     },
14799     // used when loading children.. @see loadDataFromChildren
14800     toLoadData: function(rec)
14801     {
14802         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14803         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14804         return { data : data, total : data.length };
14805         
14806     }
14807 });/*
14808  * Based on:
14809  * Ext JS Library 1.1.1
14810  * Copyright(c) 2006-2007, Ext JS, LLC.
14811  *
14812  * Originally Released Under LGPL - original licence link has changed is not relivant.
14813  *
14814  * Fork - LGPL
14815  * <script type="text/javascript">
14816  */
14817
14818 /**
14819  * @class Roo.data.ArrayReader
14820  * @extends Roo.data.DataReader
14821  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14822  * Each element of that Array represents a row of data fields. The
14823  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14824  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14825  * <p>
14826  * Example code:.
14827  * <pre><code>
14828 var RecordDef = Roo.data.Record.create([
14829     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14830     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14831 ]);
14832 var myReader = new Roo.data.ArrayReader({
14833     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14834 }, RecordDef);
14835 </code></pre>
14836  * <p>
14837  * This would consume an Array like this:
14838  * <pre><code>
14839 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14840   </code></pre>
14841  
14842  * @constructor
14843  * Create a new JsonReader
14844  * @param {Object} meta Metadata configuration options.
14845  * @param {Object|Array} recordType Either an Array of field definition objects
14846  * 
14847  * @cfg {Array} fields Array of field definition objects
14848  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14849  * as specified to {@link Roo.data.Record#create},
14850  * or an {@link Roo.data.Record} object
14851  *
14852  * 
14853  * created using {@link Roo.data.Record#create}.
14854  */
14855 Roo.data.ArrayReader = function(meta, recordType)
14856 {    
14857     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14858 };
14859
14860 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14861     
14862       /**
14863      * Create a data block containing Roo.data.Records from an XML document.
14864      * @param {Object} o An Array of row objects which represents the dataset.
14865      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14866      * a cache of Roo.data.Records.
14867      */
14868     readRecords : function(o)
14869     {
14870         var sid = this.meta ? this.meta.id : null;
14871         var recordType = this.recordType, fields = recordType.prototype.fields;
14872         var records = [];
14873         var root = o;
14874         for(var i = 0; i < root.length; i++){
14875                 var n = root[i];
14876             var values = {};
14877             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14878             for(var j = 0, jlen = fields.length; j < jlen; j++){
14879                 var f = fields.items[j];
14880                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14881                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14882                 v = f.convert(v);
14883                 values[f.name] = v;
14884             }
14885             var record = new recordType(values, id);
14886             record.json = n;
14887             records[records.length] = record;
14888         }
14889         return {
14890             records : records,
14891             totalRecords : records.length
14892         };
14893     },
14894     // used when loading children.. @see loadDataFromChildren
14895     toLoadData: function(rec)
14896     {
14897         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14898         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14899         
14900     }
14901     
14902     
14903 });/*
14904  * - LGPL
14905  * * 
14906  */
14907
14908 /**
14909  * @class Roo.bootstrap.ComboBox
14910  * @extends Roo.bootstrap.TriggerField
14911  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14912  * @cfg {Boolean} append (true|false) default false
14913  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14914  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14915  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14916  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14917  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14918  * @cfg {Boolean} animate default true
14919  * @cfg {Boolean} emptyResultText only for touch device
14920  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14921  * @cfg {String} emptyTitle default ''
14922  * @cfg {Number} width fixed with? experimental
14923  * @constructor
14924  * Create a new ComboBox.
14925  * @param {Object} config Configuration options
14926  */
14927 Roo.bootstrap.ComboBox = function(config){
14928     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14929     this.addEvents({
14930         /**
14931          * @event expand
14932          * Fires when the dropdown list is expanded
14933         * @param {Roo.bootstrap.ComboBox} combo This combo box
14934         */
14935         'expand' : true,
14936         /**
14937          * @event collapse
14938          * Fires when the dropdown list is collapsed
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         */
14941         'collapse' : true,
14942         /**
14943          * @event beforeselect
14944          * Fires before a list item is selected. Return false to cancel the selection.
14945         * @param {Roo.bootstrap.ComboBox} combo This combo box
14946         * @param {Roo.data.Record} record The data record returned from the underlying store
14947         * @param {Number} index The index of the selected item in the dropdown list
14948         */
14949         'beforeselect' : true,
14950         /**
14951          * @event select
14952          * Fires when a list item is selected
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14955         * @param {Number} index The index of the selected item in the dropdown list
14956         */
14957         'select' : true,
14958         /**
14959          * @event beforequery
14960          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14961          * The event object passed has these properties:
14962         * @param {Roo.bootstrap.ComboBox} combo This combo box
14963         * @param {String} query The query
14964         * @param {Boolean} forceAll true to force "all" query
14965         * @param {Boolean} cancel true to cancel the query
14966         * @param {Object} e The query event object
14967         */
14968         'beforequery': true,
14969          /**
14970          * @event add
14971          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'add' : true,
14975         /**
14976          * @event edit
14977          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14978         * @param {Roo.bootstrap.ComboBox} combo This combo box
14979         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14980         */
14981         'edit' : true,
14982         /**
14983          * @event remove
14984          * Fires when the remove value from the combobox array
14985         * @param {Roo.bootstrap.ComboBox} combo This combo box
14986         */
14987         'remove' : true,
14988         /**
14989          * @event afterremove
14990          * Fires when the remove value from the combobox array
14991         * @param {Roo.bootstrap.ComboBox} combo This combo box
14992         */
14993         'afterremove' : true,
14994         /**
14995          * @event specialfilter
14996          * Fires when specialfilter
14997             * @param {Roo.bootstrap.ComboBox} combo This combo box
14998             */
14999         'specialfilter' : true,
15000         /**
15001          * @event tick
15002          * Fires when tick the element
15003             * @param {Roo.bootstrap.ComboBox} combo This combo box
15004             */
15005         'tick' : true,
15006         /**
15007          * @event touchviewdisplay
15008          * Fires when touch view require special display (default is using displayField)
15009             * @param {Roo.bootstrap.ComboBox} combo This combo box
15010             * @param {Object} cfg set html .
15011             */
15012         'touchviewdisplay' : true
15013         
15014     });
15015     
15016     this.item = [];
15017     this.tickItems = [];
15018     
15019     this.selectedIndex = -1;
15020     if(this.mode == 'local'){
15021         if(config.queryDelay === undefined){
15022             this.queryDelay = 10;
15023         }
15024         if(config.minChars === undefined){
15025             this.minChars = 0;
15026         }
15027     }
15028 };
15029
15030 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15031      
15032     /**
15033      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15034      * rendering into an Roo.Editor, defaults to false)
15035      */
15036     /**
15037      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15038      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15039      */
15040     /**
15041      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15042      */
15043     /**
15044      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15045      * the dropdown list (defaults to undefined, with no header element)
15046      */
15047
15048      /**
15049      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15050      */
15051      
15052      /**
15053      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15054      */
15055     listWidth: undefined,
15056     /**
15057      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15058      * mode = 'remote' or 'text' if mode = 'local')
15059      */
15060     displayField: undefined,
15061     
15062     /**
15063      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15064      * mode = 'remote' or 'value' if mode = 'local'). 
15065      * Note: use of a valueField requires the user make a selection
15066      * in order for a value to be mapped.
15067      */
15068     valueField: undefined,
15069     /**
15070      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15071      */
15072     modalTitle : '',
15073     
15074     /**
15075      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15076      * field's data value (defaults to the underlying DOM element's name)
15077      */
15078     hiddenName: undefined,
15079     /**
15080      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15081      */
15082     listClass: '',
15083     /**
15084      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15085      */
15086     selectedClass: 'active',
15087     
15088     /**
15089      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15090      */
15091     shadow:'sides',
15092     /**
15093      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15094      * anchor positions (defaults to 'tl-bl')
15095      */
15096     listAlign: 'tl-bl?',
15097     /**
15098      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15099      */
15100     maxHeight: 300,
15101     /**
15102      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15103      * query specified by the allQuery config option (defaults to 'query')
15104      */
15105     triggerAction: 'query',
15106     /**
15107      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15108      * (defaults to 4, does not apply if editable = false)
15109      */
15110     minChars : 4,
15111     /**
15112      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15113      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15114      */
15115     typeAhead: false,
15116     /**
15117      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15118      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15119      */
15120     queryDelay: 500,
15121     /**
15122      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15123      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15124      */
15125     pageSize: 0,
15126     /**
15127      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15128      * when editable = true (defaults to false)
15129      */
15130     selectOnFocus:false,
15131     /**
15132      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15133      */
15134     queryParam: 'query',
15135     /**
15136      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15137      * when mode = 'remote' (defaults to 'Loading...')
15138      */
15139     loadingText: 'Loading...',
15140     /**
15141      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15142      */
15143     resizable: false,
15144     /**
15145      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15146      */
15147     handleHeight : 8,
15148     /**
15149      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15150      * traditional select (defaults to true)
15151      */
15152     editable: true,
15153     /**
15154      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15155      */
15156     allQuery: '',
15157     /**
15158      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15159      */
15160     mode: 'remote',
15161     /**
15162      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15163      * listWidth has a higher value)
15164      */
15165     minListWidth : 70,
15166     /**
15167      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15168      * allow the user to set arbitrary text into the field (defaults to false)
15169      */
15170     forceSelection:false,
15171     /**
15172      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15173      * if typeAhead = true (defaults to 250)
15174      */
15175     typeAheadDelay : 250,
15176     /**
15177      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15178      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15179      */
15180     valueNotFoundText : undefined,
15181     /**
15182      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15183      */
15184     blockFocus : false,
15185     
15186     /**
15187      * @cfg {Boolean} disableClear Disable showing of clear button.
15188      */
15189     disableClear : false,
15190     /**
15191      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15192      */
15193     alwaysQuery : false,
15194     
15195     /**
15196      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15197      */
15198     multiple : false,
15199     
15200     /**
15201      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15202      */
15203     invalidClass : "has-warning",
15204     
15205     /**
15206      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15207      */
15208     validClass : "has-success",
15209     
15210     /**
15211      * @cfg {Boolean} specialFilter (true|false) special filter default false
15212      */
15213     specialFilter : false,
15214     
15215     /**
15216      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15217      */
15218     mobileTouchView : true,
15219     
15220     /**
15221      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15222      */
15223     useNativeIOS : false,
15224     
15225     /**
15226      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15227      */
15228     mobile_restrict_height : false,
15229     
15230     ios_options : false,
15231     
15232     //private
15233     addicon : false,
15234     editicon: false,
15235     
15236     page: 0,
15237     hasQuery: false,
15238     append: false,
15239     loadNext: false,
15240     autoFocus : true,
15241     tickable : false,
15242     btnPosition : 'right',
15243     triggerList : true,
15244     showToggleBtn : true,
15245     animate : true,
15246     emptyResultText: 'Empty',
15247     triggerText : 'Select',
15248     emptyTitle : '',
15249     width : false,
15250     
15251     // element that contains real text value.. (when hidden is used..)
15252     
15253     getAutoCreate : function()
15254     {   
15255         var cfg = false;
15256         //render
15257         /*
15258          * Render classic select for iso
15259          */
15260         
15261         if(Roo.isIOS && this.useNativeIOS){
15262             cfg = this.getAutoCreateNativeIOS();
15263             return cfg;
15264         }
15265         
15266         /*
15267          * Touch Devices
15268          */
15269         
15270         if(Roo.isTouch && this.mobileTouchView){
15271             cfg = this.getAutoCreateTouchView();
15272             return cfg;;
15273         }
15274         
15275         /*
15276          *  Normal ComboBox
15277          */
15278         if(!this.tickable){
15279             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15280             return cfg;
15281         }
15282         
15283         /*
15284          *  ComboBox with tickable selections
15285          */
15286              
15287         var align = this.labelAlign || this.parentLabelAlign();
15288         
15289         cfg = {
15290             cls : 'form-group roo-combobox-tickable' //input-group
15291         };
15292         
15293         var btn_text_select = '';
15294         var btn_text_done = '';
15295         var btn_text_cancel = '';
15296         
15297         if (this.btn_text_show) {
15298             btn_text_select = 'Select';
15299             btn_text_done = 'Done';
15300             btn_text_cancel = 'Cancel'; 
15301         }
15302         
15303         var buttons = {
15304             tag : 'div',
15305             cls : 'tickable-buttons',
15306             cn : [
15307                 {
15308                     tag : 'button',
15309                     type : 'button',
15310                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15311                     //html : this.triggerText
15312                     html: btn_text_select
15313                 },
15314                 {
15315                     tag : 'button',
15316                     type : 'button',
15317                     name : 'ok',
15318                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15319                     //html : 'Done'
15320                     html: btn_text_done
15321                 },
15322                 {
15323                     tag : 'button',
15324                     type : 'button',
15325                     name : 'cancel',
15326                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15327                     //html : 'Cancel'
15328                     html: btn_text_cancel
15329                 }
15330             ]
15331         };
15332         
15333         if(this.editable){
15334             buttons.cn.unshift({
15335                 tag: 'input',
15336                 cls: 'roo-select2-search-field-input'
15337             });
15338         }
15339         
15340         var _this = this;
15341         
15342         Roo.each(buttons.cn, function(c){
15343             if (_this.size) {
15344                 c.cls += ' btn-' + _this.size;
15345             }
15346
15347             if (_this.disabled) {
15348                 c.disabled = true;
15349             }
15350         });
15351         
15352         var box = {
15353             tag: 'div',
15354             style : 'display: contents',
15355             cn: [
15356                 {
15357                     tag: 'input',
15358                     type : 'hidden',
15359                     cls: 'form-hidden-field'
15360                 },
15361                 {
15362                     tag: 'ul',
15363                     cls: 'roo-select2-choices',
15364                     cn:[
15365                         {
15366                             tag: 'li',
15367                             cls: 'roo-select2-search-field',
15368                             cn: [
15369                                 buttons
15370                             ]
15371                         }
15372                     ]
15373                 }
15374             ]
15375         };
15376         
15377         var combobox = {
15378             cls: 'roo-select2-container input-group roo-select2-container-multi',
15379             cn: [
15380                 
15381                 box
15382 //                {
15383 //                    tag: 'ul',
15384 //                    cls: 'typeahead typeahead-long dropdown-menu',
15385 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15386 //                }
15387             ]
15388         };
15389         
15390         if(this.hasFeedback && !this.allowBlank){
15391             
15392             var feedback = {
15393                 tag: 'span',
15394                 cls: 'glyphicon form-control-feedback'
15395             };
15396
15397             combobox.cn.push(feedback);
15398         }
15399         
15400         
15401         
15402         var indicator = {
15403             tag : 'i',
15404             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15405             tooltip : 'This field is required'
15406         };
15407         if (Roo.bootstrap.version == 4) {
15408             indicator = {
15409                 tag : 'i',
15410                 style : 'display:none'
15411             };
15412         }
15413         if (align ==='left' && this.fieldLabel.length) {
15414             
15415             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15416             
15417             cfg.cn = [
15418                 indicator,
15419                 {
15420                     tag: 'label',
15421                     'for' :  id,
15422                     cls : 'control-label col-form-label',
15423                     html : this.fieldLabel
15424
15425                 },
15426                 {
15427                     cls : "", 
15428                     cn: [
15429                         combobox
15430                     ]
15431                 }
15432
15433             ];
15434             
15435             var labelCfg = cfg.cn[1];
15436             var contentCfg = cfg.cn[2];
15437             
15438
15439             if(this.indicatorpos == 'right'){
15440                 
15441                 cfg.cn = [
15442                     {
15443                         tag: 'label',
15444                         'for' :  id,
15445                         cls : 'control-label col-form-label',
15446                         cn : [
15447                             {
15448                                 tag : 'span',
15449                                 html : this.fieldLabel
15450                             },
15451                             indicator
15452                         ]
15453                     },
15454                     {
15455                         cls : "",
15456                         cn: [
15457                             combobox
15458                         ]
15459                     }
15460
15461                 ];
15462                 
15463                 
15464                 
15465                 labelCfg = cfg.cn[0];
15466                 contentCfg = cfg.cn[1];
15467             
15468             }
15469             
15470             if(this.labelWidth > 12){
15471                 labelCfg.style = "width: " + this.labelWidth + 'px';
15472             }
15473             if(this.width * 1 > 0){
15474                 contentCfg.style = "width: " + this.width + 'px';
15475             }
15476             if(this.labelWidth < 13 && this.labelmd == 0){
15477                 this.labelmd = this.labelWidth;
15478             }
15479             
15480             if(this.labellg > 0){
15481                 labelCfg.cls += ' col-lg-' + this.labellg;
15482                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15483             }
15484             
15485             if(this.labelmd > 0){
15486                 labelCfg.cls += ' col-md-' + this.labelmd;
15487                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15488             }
15489             
15490             if(this.labelsm > 0){
15491                 labelCfg.cls += ' col-sm-' + this.labelsm;
15492                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15493             }
15494             
15495             if(this.labelxs > 0){
15496                 labelCfg.cls += ' col-xs-' + this.labelxs;
15497                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15498             }
15499                 
15500                 
15501         } else if ( this.fieldLabel.length) {
15502 //                Roo.log(" label");
15503                  cfg.cn = [
15504                    indicator,
15505                     {
15506                         tag: 'label',
15507                         //cls : 'input-group-addon',
15508                         html : this.fieldLabel
15509                     },
15510                     combobox
15511                 ];
15512                 
15513                 if(this.indicatorpos == 'right'){
15514                     cfg.cn = [
15515                         {
15516                             tag: 'label',
15517                             //cls : 'input-group-addon',
15518                             html : this.fieldLabel
15519                         },
15520                         indicator,
15521                         combobox
15522                     ];
15523                     
15524                 }
15525
15526         } else {
15527             
15528 //                Roo.log(" no label && no align");
15529                 cfg = combobox
15530                      
15531                 
15532         }
15533          
15534         var settings=this;
15535         ['xs','sm','md','lg'].map(function(size){
15536             if (settings[size]) {
15537                 cfg.cls += ' col-' + size + '-' + settings[size];
15538             }
15539         });
15540         
15541         return cfg;
15542         
15543     },
15544     
15545     _initEventsCalled : false,
15546     
15547     // private
15548     initEvents: function()
15549     {   
15550         if (this._initEventsCalled) { // as we call render... prevent looping...
15551             return;
15552         }
15553         this._initEventsCalled = true;
15554         
15555         if (!this.store) {
15556             throw "can not find store for combo";
15557         }
15558         
15559         this.indicator = this.indicatorEl();
15560         
15561         this.store = Roo.factory(this.store, Roo.data);
15562         this.store.parent = this;
15563         
15564         // if we are building from html. then this element is so complex, that we can not really
15565         // use the rendered HTML.
15566         // so we have to trash and replace the previous code.
15567         if (Roo.XComponent.build_from_html) {
15568             // remove this element....
15569             var e = this.el.dom, k=0;
15570             while (e ) { e = e.previousSibling;  ++k;}
15571
15572             this.el.remove();
15573             
15574             this.el=false;
15575             this.rendered = false;
15576             
15577             this.render(this.parent().getChildContainer(true), k);
15578         }
15579         
15580         if(Roo.isIOS && this.useNativeIOS){
15581             this.initIOSView();
15582             return;
15583         }
15584         
15585         /*
15586          * Touch Devices
15587          */
15588         
15589         if(Roo.isTouch && this.mobileTouchView){
15590             this.initTouchView();
15591             return;
15592         }
15593         
15594         if(this.tickable){
15595             this.initTickableEvents();
15596             return;
15597         }
15598         
15599         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15600         
15601         if(this.hiddenName){
15602             
15603             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15604             
15605             this.hiddenField.dom.value =
15606                 this.hiddenValue !== undefined ? this.hiddenValue :
15607                 this.value !== undefined ? this.value : '';
15608
15609             // prevent input submission
15610             this.el.dom.removeAttribute('name');
15611             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15612              
15613              
15614         }
15615         //if(Roo.isGecko){
15616         //    this.el.dom.setAttribute('autocomplete', 'off');
15617         //}
15618         
15619         var cls = 'x-combo-list';
15620         
15621         //this.list = new Roo.Layer({
15622         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15623         //});
15624         
15625         var _this = this;
15626         
15627         (function(){
15628             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15629             _this.list.setWidth(lw);
15630         }).defer(100);
15631         
15632         this.list.on('mouseover', this.onViewOver, this);
15633         this.list.on('mousemove', this.onViewMove, this);
15634         this.list.on('scroll', this.onViewScroll, this);
15635         
15636         /*
15637         this.list.swallowEvent('mousewheel');
15638         this.assetHeight = 0;
15639
15640         if(this.title){
15641             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15642             this.assetHeight += this.header.getHeight();
15643         }
15644
15645         this.innerList = this.list.createChild({cls:cls+'-inner'});
15646         this.innerList.on('mouseover', this.onViewOver, this);
15647         this.innerList.on('mousemove', this.onViewMove, this);
15648         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15649         
15650         if(this.allowBlank && !this.pageSize && !this.disableClear){
15651             this.footer = this.list.createChild({cls:cls+'-ft'});
15652             this.pageTb = new Roo.Toolbar(this.footer);
15653            
15654         }
15655         if(this.pageSize){
15656             this.footer = this.list.createChild({cls:cls+'-ft'});
15657             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15658                     {pageSize: this.pageSize});
15659             
15660         }
15661         
15662         if (this.pageTb && this.allowBlank && !this.disableClear) {
15663             var _this = this;
15664             this.pageTb.add(new Roo.Toolbar.Fill(), {
15665                 cls: 'x-btn-icon x-btn-clear',
15666                 text: '&#160;',
15667                 handler: function()
15668                 {
15669                     _this.collapse();
15670                     _this.clearValue();
15671                     _this.onSelect(false, -1);
15672                 }
15673             });
15674         }
15675         if (this.footer) {
15676             this.assetHeight += this.footer.getHeight();
15677         }
15678         */
15679             
15680         if(!this.tpl){
15681             this.tpl = Roo.bootstrap.version == 4 ?
15682                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15683                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15684         }
15685
15686         this.view = new Roo.View(this.list, this.tpl, {
15687             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15688         });
15689         //this.view.wrapEl.setDisplayed(false);
15690         this.view.on('click', this.onViewClick, this);
15691         
15692         
15693         this.store.on('beforeload', this.onBeforeLoad, this);
15694         this.store.on('load', this.onLoad, this);
15695         this.store.on('loadexception', this.onLoadException, this);
15696         /*
15697         if(this.resizable){
15698             this.resizer = new Roo.Resizable(this.list,  {
15699                pinned:true, handles:'se'
15700             });
15701             this.resizer.on('resize', function(r, w, h){
15702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15703                 this.listWidth = w;
15704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15705                 this.restrictHeight();
15706             }, this);
15707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15708         }
15709         */
15710         if(!this.editable){
15711             this.editable = true;
15712             this.setEditable(false);
15713         }
15714         
15715         /*
15716         
15717         if (typeof(this.events.add.listeners) != 'undefined') {
15718             
15719             this.addicon = this.wrap.createChild(
15720                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15721        
15722             this.addicon.on('click', function(e) {
15723                 this.fireEvent('add', this);
15724             }, this);
15725         }
15726         if (typeof(this.events.edit.listeners) != 'undefined') {
15727             
15728             this.editicon = this.wrap.createChild(
15729                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15730             if (this.addicon) {
15731                 this.editicon.setStyle('margin-left', '40px');
15732             }
15733             this.editicon.on('click', function(e) {
15734                 
15735                 // we fire even  if inothing is selected..
15736                 this.fireEvent('edit', this, this.lastData );
15737                 
15738             }, this);
15739         }
15740         */
15741         
15742         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15743             "up" : function(e){
15744                 this.inKeyMode = true;
15745                 this.selectPrev();
15746             },
15747
15748             "down" : function(e){
15749                 if(!this.isExpanded()){
15750                     this.onTriggerClick();
15751                 }else{
15752                     this.inKeyMode = true;
15753                     this.selectNext();
15754                 }
15755             },
15756
15757             "enter" : function(e){
15758 //                this.onViewClick();
15759                 //return true;
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             "esc" : function(e){
15770                 this.collapse();
15771             },
15772
15773             "tab" : function(e){
15774                 this.collapse();
15775                 
15776                 if(this.fireEvent("specialkey", this, e)){
15777                     this.onViewClick(false);
15778                 }
15779                 
15780                 return true;
15781             },
15782
15783             scope : this,
15784
15785             doRelay : function(foo, bar, hname){
15786                 if(hname == 'down' || this.scope.isExpanded()){
15787                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15788                 }
15789                 return true;
15790             },
15791
15792             forceKeyDown: true
15793         });
15794         
15795         
15796         this.queryDelay = Math.max(this.queryDelay || 10,
15797                 this.mode == 'local' ? 10 : 250);
15798         
15799         
15800         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15801         
15802         if(this.typeAhead){
15803             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15804         }
15805         if(this.editable !== false){
15806             this.inputEl().on("keyup", this.onKeyUp, this);
15807         }
15808         if(this.forceSelection){
15809             this.inputEl().on('blur', this.doForce, this);
15810         }
15811         
15812         if(this.multiple){
15813             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15814             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15815         }
15816     },
15817     
15818     initTickableEvents: function()
15819     {   
15820         this.createList();
15821         
15822         if(this.hiddenName){
15823             
15824             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15825             
15826             this.hiddenField.dom.value =
15827                 this.hiddenValue !== undefined ? this.hiddenValue :
15828                 this.value !== undefined ? this.value : '';
15829
15830             // prevent input submission
15831             this.el.dom.removeAttribute('name');
15832             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15833              
15834              
15835         }
15836         
15837 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15838         
15839         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15840         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15841         if(this.triggerList){
15842             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15843         }
15844          
15845         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15846         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15847         
15848         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15849         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15850         
15851         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15852         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15853         
15854         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15855         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15856         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15857         
15858         this.okBtn.hide();
15859         this.cancelBtn.hide();
15860         
15861         var _this = this;
15862         
15863         (function(){
15864             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15865             _this.list.setWidth(lw);
15866         }).defer(100);
15867         
15868         this.list.on('mouseover', this.onViewOver, this);
15869         this.list.on('mousemove', this.onViewMove, this);
15870         
15871         this.list.on('scroll', this.onViewScroll, this);
15872         
15873         if(!this.tpl){
15874             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15875                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15876         }
15877
15878         this.view = new Roo.View(this.list, this.tpl, {
15879             singleSelect:true,
15880             tickable:true,
15881             parent:this,
15882             store: this.store,
15883             selectedClass: this.selectedClass
15884         });
15885         
15886         //this.view.wrapEl.setDisplayed(false);
15887         this.view.on('click', this.onViewClick, this);
15888         
15889         
15890         
15891         this.store.on('beforeload', this.onBeforeLoad, this);
15892         this.store.on('load', this.onLoad, this);
15893         this.store.on('loadexception', this.onLoadException, this);
15894         
15895         if(this.editable){
15896             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15897                 "up" : function(e){
15898                     this.inKeyMode = true;
15899                     this.selectPrev();
15900                 },
15901
15902                 "down" : function(e){
15903                     this.inKeyMode = true;
15904                     this.selectNext();
15905                 },
15906
15907                 "enter" : function(e){
15908                     if(this.fireEvent("specialkey", this, e)){
15909                         this.onViewClick(false);
15910                     }
15911                     
15912                     return true;
15913                 },
15914
15915                 "esc" : function(e){
15916                     this.onTickableFooterButtonClick(e, false, false);
15917                 },
15918
15919                 "tab" : function(e){
15920                     this.fireEvent("specialkey", this, e);
15921                     
15922                     this.onTickableFooterButtonClick(e, false, false);
15923                     
15924                     return true;
15925                 },
15926
15927                 scope : this,
15928
15929                 doRelay : function(e, fn, key){
15930                     if(this.scope.isExpanded()){
15931                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15932                     }
15933                     return true;
15934                 },
15935
15936                 forceKeyDown: true
15937             });
15938         }
15939         
15940         this.queryDelay = Math.max(this.queryDelay || 10,
15941                 this.mode == 'local' ? 10 : 250);
15942         
15943         
15944         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15945         
15946         if(this.typeAhead){
15947             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15948         }
15949         
15950         if(this.editable !== false){
15951             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15952         }
15953         
15954         this.indicator = this.indicatorEl();
15955         
15956         if(this.indicator){
15957             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15958             this.indicator.hide();
15959         }
15960         
15961     },
15962
15963     onDestroy : function(){
15964         if(this.view){
15965             this.view.setStore(null);
15966             this.view.el.removeAllListeners();
15967             this.view.el.remove();
15968             this.view.purgeListeners();
15969         }
15970         if(this.list){
15971             this.list.dom.innerHTML  = '';
15972         }
15973         
15974         if(this.store){
15975             this.store.un('beforeload', this.onBeforeLoad, this);
15976             this.store.un('load', this.onLoad, this);
15977             this.store.un('loadexception', this.onLoadException, this);
15978         }
15979         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15980     },
15981
15982     // private
15983     fireKey : function(e){
15984         if(e.isNavKeyPress() && !this.list.isVisible()){
15985             this.fireEvent("specialkey", this, e);
15986         }
15987     },
15988
15989     // private
15990     onResize: function(w, h)
15991     {
15992         
15993         
15994 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15995 //        
15996 //        if(typeof w != 'number'){
15997 //            // we do not handle it!?!?
15998 //            return;
15999 //        }
16000 //        var tw = this.trigger.getWidth();
16001 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16002 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16003 //        var x = w - tw;
16004 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16005 //            
16006 //        //this.trigger.setStyle('left', x+'px');
16007 //        
16008 //        if(this.list && this.listWidth === undefined){
16009 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16010 //            this.list.setWidth(lw);
16011 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16012 //        }
16013         
16014     
16015         
16016     },
16017
16018     /**
16019      * Allow or prevent the user from directly editing the field text.  If false is passed,
16020      * the user will only be able to select from the items defined in the dropdown list.  This method
16021      * is the runtime equivalent of setting the 'editable' config option at config time.
16022      * @param {Boolean} value True to allow the user to directly edit the field text
16023      */
16024     setEditable : function(value){
16025         if(value == this.editable){
16026             return;
16027         }
16028         this.editable = value;
16029         if(!value){
16030             this.inputEl().dom.setAttribute('readOnly', true);
16031             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16032             this.inputEl().addClass('x-combo-noedit');
16033         }else{
16034             this.inputEl().dom.setAttribute('readOnly', false);
16035             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16036             this.inputEl().removeClass('x-combo-noedit');
16037         }
16038     },
16039
16040     // private
16041     
16042     onBeforeLoad : function(combo,opts){
16043         if(!this.hasFocus){
16044             return;
16045         }
16046          if (!opts.add) {
16047             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16048          }
16049         this.restrictHeight();
16050         this.selectedIndex = -1;
16051     },
16052
16053     // private
16054     onLoad : function(){
16055         
16056         this.hasQuery = false;
16057         
16058         if(!this.hasFocus){
16059             return;
16060         }
16061         
16062         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16063             this.loading.hide();
16064         }
16065         
16066         if(this.store.getCount() > 0){
16067             
16068             this.expand();
16069             this.restrictHeight();
16070             if(this.lastQuery == this.allQuery){
16071                 if(this.editable && !this.tickable){
16072                     this.inputEl().dom.select();
16073                 }
16074                 
16075                 if(
16076                     !this.selectByValue(this.value, true) &&
16077                     this.autoFocus && 
16078                     (
16079                         !this.store.lastOptions ||
16080                         typeof(this.store.lastOptions.add) == 'undefined' || 
16081                         this.store.lastOptions.add != true
16082                     )
16083                 ){
16084                     this.select(0, true);
16085                 }
16086             }else{
16087                 if(this.autoFocus){
16088                     this.selectNext();
16089                 }
16090                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16091                     this.taTask.delay(this.typeAheadDelay);
16092                 }
16093             }
16094         }else{
16095             this.onEmptyResults();
16096         }
16097         
16098         //this.el.focus();
16099     },
16100     // private
16101     onLoadException : function()
16102     {
16103         this.hasQuery = false;
16104         
16105         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16106             this.loading.hide();
16107         }
16108         
16109         if(this.tickable && this.editable){
16110             return;
16111         }
16112         
16113         this.collapse();
16114         // only causes errors at present
16115         //Roo.log(this.store.reader.jsonData);
16116         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16117             // fixme
16118             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16119         //}
16120         
16121         
16122     },
16123     // private
16124     onTypeAhead : function(){
16125         if(this.store.getCount() > 0){
16126             var r = this.store.getAt(0);
16127             var newValue = r.data[this.displayField];
16128             var len = newValue.length;
16129             var selStart = this.getRawValue().length;
16130             
16131             if(selStart != len){
16132                 this.setRawValue(newValue);
16133                 this.selectText(selStart, newValue.length);
16134             }
16135         }
16136     },
16137
16138     // private
16139     onSelect : function(record, index){
16140         
16141         if(this.fireEvent('beforeselect', this, record, index) !== false){
16142         
16143             this.setFromData(index > -1 ? record.data : false);
16144             
16145             this.collapse();
16146             this.fireEvent('select', this, record, index);
16147         }
16148     },
16149
16150     /**
16151      * Returns the currently selected field value or empty string if no value is set.
16152      * @return {String} value The selected value
16153      */
16154     getValue : function()
16155     {
16156         if(Roo.isIOS && this.useNativeIOS){
16157             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16158         }
16159         
16160         if(this.multiple){
16161             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16162         }
16163         
16164         if(this.valueField){
16165             return typeof this.value != 'undefined' ? this.value : '';
16166         }else{
16167             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16168         }
16169     },
16170     
16171     getRawValue : function()
16172     {
16173         if(Roo.isIOS && this.useNativeIOS){
16174             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16175         }
16176         
16177         var v = this.inputEl().getValue();
16178         
16179         return v;
16180     },
16181
16182     /**
16183      * Clears any text/value currently set in the field
16184      */
16185     clearValue : function(){
16186         
16187         if(this.hiddenField){
16188             this.hiddenField.dom.value = '';
16189         }
16190         this.value = '';
16191         this.setRawValue('');
16192         this.lastSelectionText = '';
16193         this.lastData = false;
16194         
16195         var close = this.closeTriggerEl();
16196         
16197         if(close){
16198             close.hide();
16199         }
16200         
16201         this.validate();
16202         
16203     },
16204
16205     /**
16206      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16207      * will be displayed in the field.  If the value does not match the data value of an existing item,
16208      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16209      * Otherwise the field will be blank (although the value will still be set).
16210      * @param {String} value The value to match
16211      */
16212     setValue : function(v)
16213     {
16214         if(Roo.isIOS && this.useNativeIOS){
16215             this.setIOSValue(v);
16216             return;
16217         }
16218         
16219         if(this.multiple){
16220             this.syncValue();
16221             return;
16222         }
16223         
16224         var text = v;
16225         if(this.valueField){
16226             var r = this.findRecord(this.valueField, v);
16227             if(r){
16228                 text = r.data[this.displayField];
16229             }else if(this.valueNotFoundText !== undefined){
16230                 text = this.valueNotFoundText;
16231             }
16232         }
16233         this.lastSelectionText = text;
16234         if(this.hiddenField){
16235             this.hiddenField.dom.value = v;
16236         }
16237         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16238         this.value = v;
16239         
16240         var close = this.closeTriggerEl();
16241         
16242         if(close){
16243             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16244         }
16245         
16246         this.validate();
16247     },
16248     /**
16249      * @property {Object} the last set data for the element
16250      */
16251     
16252     lastData : false,
16253     /**
16254      * Sets the value of the field based on a object which is related to the record format for the store.
16255      * @param {Object} value the value to set as. or false on reset?
16256      */
16257     setFromData : function(o){
16258         
16259         if(this.multiple){
16260             this.addItem(o);
16261             return;
16262         }
16263             
16264         var dv = ''; // display value
16265         var vv = ''; // value value..
16266         this.lastData = o;
16267         if (this.displayField) {
16268             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16269         } else {
16270             // this is an error condition!!!
16271             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16272         }
16273         
16274         if(this.valueField){
16275             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16276         }
16277         
16278         var close = this.closeTriggerEl();
16279         
16280         if(close){
16281             if(dv.length || vv * 1 > 0){
16282                 close.show() ;
16283                 this.blockFocus=true;
16284             } else {
16285                 close.hide();
16286             }             
16287         }
16288         
16289         if(this.hiddenField){
16290             this.hiddenField.dom.value = vv;
16291             
16292             this.lastSelectionText = dv;
16293             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16294             this.value = vv;
16295             return;
16296         }
16297         // no hidden field.. - we store the value in 'value', but still display
16298         // display field!!!!
16299         this.lastSelectionText = dv;
16300         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16301         this.value = vv;
16302         
16303         
16304         
16305     },
16306     // private
16307     reset : function(){
16308         // overridden so that last data is reset..
16309         
16310         if(this.multiple){
16311             this.clearItem();
16312             return;
16313         }
16314         
16315         this.setValue(this.originalValue);
16316         //this.clearInvalid();
16317         this.lastData = false;
16318         if (this.view) {
16319             this.view.clearSelections();
16320         }
16321         
16322         this.validate();
16323     },
16324     // private
16325     findRecord : function(prop, value){
16326         var record;
16327         if(this.store.getCount() > 0){
16328             this.store.each(function(r){
16329                 if(r.data[prop] == value){
16330                     record = r;
16331                     return false;
16332                 }
16333                 return true;
16334             });
16335         }
16336         return record;
16337     },
16338     
16339     getName: function()
16340     {
16341         // returns hidden if it's set..
16342         if (!this.rendered) {return ''};
16343         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16344         
16345     },
16346     // private
16347     onViewMove : function(e, t){
16348         this.inKeyMode = false;
16349     },
16350
16351     // private
16352     onViewOver : function(e, t){
16353         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16354             return;
16355         }
16356         var item = this.view.findItemFromChild(t);
16357         
16358         if(item){
16359             var index = this.view.indexOf(item);
16360             this.select(index, false);
16361         }
16362     },
16363
16364     // private
16365     onViewClick : function(view, doFocus, el, e)
16366     {
16367         var index = this.view.getSelectedIndexes()[0];
16368         
16369         var r = this.store.getAt(index);
16370         
16371         if(this.tickable){
16372             
16373             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16374                 return;
16375             }
16376             
16377             var rm = false;
16378             var _this = this;
16379             
16380             Roo.each(this.tickItems, function(v,k){
16381                 
16382                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16383                     Roo.log(v);
16384                     _this.tickItems.splice(k, 1);
16385                     
16386                     if(typeof(e) == 'undefined' && view == false){
16387                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16388                     }
16389                     
16390                     rm = true;
16391                     return;
16392                 }
16393             });
16394             
16395             if(rm){
16396                 return;
16397             }
16398             
16399             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16400                 this.tickItems.push(r.data);
16401             }
16402             
16403             if(typeof(e) == 'undefined' && view == false){
16404                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16405             }
16406                     
16407             return;
16408         }
16409         
16410         if(r){
16411             this.onSelect(r, index);
16412         }
16413         if(doFocus !== false && !this.blockFocus){
16414             this.inputEl().focus();
16415         }
16416     },
16417
16418     // private
16419     restrictHeight : function(){
16420         //this.innerList.dom.style.height = '';
16421         //var inner = this.innerList.dom;
16422         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16423         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16424         //this.list.beginUpdate();
16425         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16426         this.list.alignTo(this.inputEl(), this.listAlign);
16427         this.list.alignTo(this.inputEl(), this.listAlign);
16428         //this.list.endUpdate();
16429     },
16430
16431     // private
16432     onEmptyResults : function(){
16433         
16434         if(this.tickable && this.editable){
16435             this.hasFocus = false;
16436             this.restrictHeight();
16437             return;
16438         }
16439         
16440         this.collapse();
16441     },
16442
16443     /**
16444      * Returns true if the dropdown list is expanded, else false.
16445      */
16446     isExpanded : function(){
16447         return this.list.isVisible();
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {String} value The data value of the item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      * @return {Boolean} True if the value matched an item in the list, else false
16457      */
16458     selectByValue : function(v, scrollIntoView){
16459         if(v !== undefined && v !== null){
16460             var r = this.findRecord(this.valueField || this.displayField, v);
16461             if(r){
16462                 this.select(this.store.indexOf(r), scrollIntoView);
16463                 return true;
16464             }
16465         }
16466         return false;
16467     },
16468
16469     /**
16470      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16471      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16472      * @param {Number} index The zero-based index of the list item to select
16473      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16474      * selected item if it is not currently in view (defaults to true)
16475      */
16476     select : function(index, scrollIntoView){
16477         this.selectedIndex = index;
16478         this.view.select(index);
16479         if(scrollIntoView !== false){
16480             var el = this.view.getNode(index);
16481             /*
16482              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16483              */
16484             if(el){
16485                 this.list.scrollChildIntoView(el, false);
16486             }
16487         }
16488     },
16489
16490     // private
16491     selectNext : function(){
16492         var ct = this.store.getCount();
16493         if(ct > 0){
16494             if(this.selectedIndex == -1){
16495                 this.select(0);
16496             }else if(this.selectedIndex < ct-1){
16497                 this.select(this.selectedIndex+1);
16498             }
16499         }
16500     },
16501
16502     // private
16503     selectPrev : function(){
16504         var ct = this.store.getCount();
16505         if(ct > 0){
16506             if(this.selectedIndex == -1){
16507                 this.select(0);
16508             }else if(this.selectedIndex != 0){
16509                 this.select(this.selectedIndex-1);
16510             }
16511         }
16512     },
16513
16514     // private
16515     onKeyUp : function(e){
16516         if(this.editable !== false && !e.isSpecialKey()){
16517             this.lastKey = e.getKey();
16518             this.dqTask.delay(this.queryDelay);
16519         }
16520     },
16521
16522     // private
16523     validateBlur : function(){
16524         return !this.list || !this.list.isVisible();   
16525     },
16526
16527     // private
16528     initQuery : function(){
16529         
16530         var v = this.getRawValue();
16531         
16532         if(this.tickable && this.editable){
16533             v = this.tickableInputEl().getValue();
16534         }
16535         
16536         this.doQuery(v);
16537     },
16538
16539     // private
16540     doForce : function(){
16541         if(this.inputEl().dom.value.length > 0){
16542             this.inputEl().dom.value =
16543                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16544              
16545         }
16546     },
16547
16548     /**
16549      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16550      * query allowing the query action to be canceled if needed.
16551      * @param {String} query The SQL query to execute
16552      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16553      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16554      * saved in the current store (defaults to false)
16555      */
16556     doQuery : function(q, forceAll){
16557         
16558         if(q === undefined || q === null){
16559             q = '';
16560         }
16561         var qe = {
16562             query: q,
16563             forceAll: forceAll,
16564             combo: this,
16565             cancel:false
16566         };
16567         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16568             return false;
16569         }
16570         q = qe.query;
16571         
16572         forceAll = qe.forceAll;
16573         if(forceAll === true || (q.length >= this.minChars)){
16574             
16575             this.hasQuery = true;
16576             
16577             if(this.lastQuery != q || this.alwaysQuery){
16578                 this.lastQuery = q;
16579                 if(this.mode == 'local'){
16580                     this.selectedIndex = -1;
16581                     if(forceAll){
16582                         this.store.clearFilter();
16583                     }else{
16584                         
16585                         if(this.specialFilter){
16586                             this.fireEvent('specialfilter', this);
16587                             this.onLoad();
16588                             return;
16589                         }
16590                         
16591                         this.store.filter(this.displayField, q);
16592                     }
16593                     
16594                     this.store.fireEvent("datachanged", this.store);
16595                     
16596                     this.onLoad();
16597                     
16598                     
16599                 }else{
16600                     
16601                     this.store.baseParams[this.queryParam] = q;
16602                     
16603                     var options = {params : this.getParams(q)};
16604                     
16605                     if(this.loadNext){
16606                         options.add = true;
16607                         options.params.start = this.page * this.pageSize;
16608                     }
16609                     
16610                     this.store.load(options);
16611                     
16612                     /*
16613                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16614                      *  we should expand the list on onLoad
16615                      *  so command out it
16616                      */
16617 //                    this.expand();
16618                 }
16619             }else{
16620                 this.selectedIndex = -1;
16621                 this.onLoad();   
16622             }
16623         }
16624         
16625         this.loadNext = false;
16626     },
16627     
16628     // private
16629     getParams : function(q){
16630         var p = {};
16631         //p[this.queryParam] = q;
16632         
16633         if(this.pageSize){
16634             p.start = 0;
16635             p.limit = this.pageSize;
16636         }
16637         return p;
16638     },
16639
16640     /**
16641      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16642      */
16643     collapse : function(){
16644         if(!this.isExpanded()){
16645             return;
16646         }
16647         
16648         this.list.hide();
16649         
16650         this.hasFocus = false;
16651         
16652         if(this.tickable){
16653             this.okBtn.hide();
16654             this.cancelBtn.hide();
16655             this.trigger.show();
16656             
16657             if(this.editable){
16658                 this.tickableInputEl().dom.value = '';
16659                 this.tickableInputEl().blur();
16660             }
16661             
16662         }
16663         
16664         Roo.get(document).un('mousedown', this.collapseIf, this);
16665         Roo.get(document).un('mousewheel', this.collapseIf, this);
16666         if (!this.editable) {
16667             Roo.get(document).un('keydown', this.listKeyPress, this);
16668         }
16669         this.fireEvent('collapse', this);
16670         
16671         this.validate();
16672     },
16673
16674     // private
16675     collapseIf : function(e){
16676         var in_combo  = e.within(this.el);
16677         var in_list =  e.within(this.list);
16678         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16679         
16680         if (in_combo || in_list || is_list) {
16681             //e.stopPropagation();
16682             return;
16683         }
16684         
16685         if(this.tickable){
16686             this.onTickableFooterButtonClick(e, false, false);
16687         }
16688
16689         this.collapse();
16690         
16691     },
16692
16693     /**
16694      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16695      */
16696     expand : function(){
16697        
16698         if(this.isExpanded() || !this.hasFocus){
16699             return;
16700         }
16701         
16702         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16703         this.list.setWidth(lw);
16704         
16705         Roo.log('expand');
16706         
16707         this.list.show();
16708         
16709         this.restrictHeight();
16710         
16711         if(this.tickable){
16712             
16713             this.tickItems = Roo.apply([], this.item);
16714             
16715             this.okBtn.show();
16716             this.cancelBtn.show();
16717             this.trigger.hide();
16718             
16719             if(this.editable){
16720                 this.tickableInputEl().focus();
16721             }
16722             
16723         }
16724         
16725         Roo.get(document).on('mousedown', this.collapseIf, this);
16726         Roo.get(document).on('mousewheel', this.collapseIf, this);
16727         if (!this.editable) {
16728             Roo.get(document).on('keydown', this.listKeyPress, this);
16729         }
16730         
16731         this.fireEvent('expand', this);
16732     },
16733
16734     // private
16735     // Implements the default empty TriggerField.onTriggerClick function
16736     onTriggerClick : function(e)
16737     {
16738         Roo.log('trigger click');
16739         
16740         if(this.disabled || !this.triggerList){
16741             return;
16742         }
16743         
16744         this.page = 0;
16745         this.loadNext = false;
16746         
16747         if(this.isExpanded()){
16748             this.collapse();
16749             if (!this.blockFocus) {
16750                 this.inputEl().focus();
16751             }
16752             
16753         }else {
16754             this.hasFocus = true;
16755             if(this.triggerAction == 'all') {
16756                 this.doQuery(this.allQuery, true);
16757             } else {
16758                 this.doQuery(this.getRawValue());
16759             }
16760             if (!this.blockFocus) {
16761                 this.inputEl().focus();
16762             }
16763         }
16764     },
16765     
16766     onTickableTriggerClick : function(e)
16767     {
16768         if(this.disabled){
16769             return;
16770         }
16771         
16772         this.page = 0;
16773         this.loadNext = false;
16774         this.hasFocus = true;
16775         
16776         if(this.triggerAction == 'all') {
16777             this.doQuery(this.allQuery, true);
16778         } else {
16779             this.doQuery(this.getRawValue());
16780         }
16781     },
16782     
16783     onSearchFieldClick : function(e)
16784     {
16785         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16786             this.onTickableFooterButtonClick(e, false, false);
16787             return;
16788         }
16789         
16790         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16791             return;
16792         }
16793         
16794         this.page = 0;
16795         this.loadNext = false;
16796         this.hasFocus = true;
16797         
16798         if(this.triggerAction == 'all') {
16799             this.doQuery(this.allQuery, true);
16800         } else {
16801             this.doQuery(this.getRawValue());
16802         }
16803     },
16804     
16805     listKeyPress : function(e)
16806     {
16807         //Roo.log('listkeypress');
16808         // scroll to first matching element based on key pres..
16809         if (e.isSpecialKey()) {
16810             return false;
16811         }
16812         var k = String.fromCharCode(e.getKey()).toUpperCase();
16813         //Roo.log(k);
16814         var match  = false;
16815         var csel = this.view.getSelectedNodes();
16816         var cselitem = false;
16817         if (csel.length) {
16818             var ix = this.view.indexOf(csel[0]);
16819             cselitem  = this.store.getAt(ix);
16820             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16821                 cselitem = false;
16822             }
16823             
16824         }
16825         
16826         this.store.each(function(v) { 
16827             if (cselitem) {
16828                 // start at existing selection.
16829                 if (cselitem.id == v.id) {
16830                     cselitem = false;
16831                 }
16832                 return true;
16833             }
16834                 
16835             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16836                 match = this.store.indexOf(v);
16837                 return false;
16838             }
16839             return true;
16840         }, this);
16841         
16842         if (match === false) {
16843             return true; // no more action?
16844         }
16845         // scroll to?
16846         this.view.select(match);
16847         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16848         sn.scrollIntoView(sn.dom.parentNode, false);
16849     },
16850     
16851     onViewScroll : function(e, t){
16852         
16853         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){
16854             return;
16855         }
16856         
16857         this.hasQuery = true;
16858         
16859         this.loading = this.list.select('.loading', true).first();
16860         
16861         if(this.loading === null){
16862             this.list.createChild({
16863                 tag: 'div',
16864                 cls: 'loading roo-select2-more-results roo-select2-active',
16865                 html: 'Loading more results...'
16866             });
16867             
16868             this.loading = this.list.select('.loading', true).first();
16869             
16870             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16871             
16872             this.loading.hide();
16873         }
16874         
16875         this.loading.show();
16876         
16877         var _combo = this;
16878         
16879         this.page++;
16880         this.loadNext = true;
16881         
16882         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16883         
16884         return;
16885     },
16886     
16887     addItem : function(o)
16888     {   
16889         var dv = ''; // display value
16890         
16891         if (this.displayField) {
16892             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16893         } else {
16894             // this is an error condition!!!
16895             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16896         }
16897         
16898         if(!dv.length){
16899             return;
16900         }
16901         
16902         var choice = this.choices.createChild({
16903             tag: 'li',
16904             cls: 'roo-select2-search-choice',
16905             cn: [
16906                 {
16907                     tag: 'div',
16908                     html: dv
16909                 },
16910                 {
16911                     tag: 'a',
16912                     href: '#',
16913                     cls: 'roo-select2-search-choice-close fa fa-times',
16914                     tabindex: '-1'
16915                 }
16916             ]
16917             
16918         }, this.searchField);
16919         
16920         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16921         
16922         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16923         
16924         this.item.push(o);
16925         
16926         this.lastData = o;
16927         
16928         this.syncValue();
16929         
16930         this.inputEl().dom.value = '';
16931         
16932         this.validate();
16933     },
16934     
16935     onRemoveItem : function(e, _self, o)
16936     {
16937         e.preventDefault();
16938         
16939         this.lastItem = Roo.apply([], this.item);
16940         
16941         var index = this.item.indexOf(o.data) * 1;
16942         
16943         if( index < 0){
16944             Roo.log('not this item?!');
16945             return;
16946         }
16947         
16948         this.item.splice(index, 1);
16949         o.item.remove();
16950         
16951         this.syncValue();
16952         
16953         this.fireEvent('remove', this, e);
16954         
16955         this.validate();
16956         
16957     },
16958     
16959     syncValue : function()
16960     {
16961         if(!this.item.length){
16962             this.clearValue();
16963             return;
16964         }
16965             
16966         var value = [];
16967         var _this = this;
16968         Roo.each(this.item, function(i){
16969             if(_this.valueField){
16970                 value.push(i[_this.valueField]);
16971                 return;
16972             }
16973
16974             value.push(i);
16975         });
16976
16977         this.value = value.join(',');
16978
16979         if(this.hiddenField){
16980             this.hiddenField.dom.value = this.value;
16981         }
16982         
16983         this.store.fireEvent("datachanged", this.store);
16984         
16985         this.validate();
16986     },
16987     
16988     clearItem : function()
16989     {
16990         if(!this.multiple){
16991             return;
16992         }
16993         
16994         this.item = [];
16995         
16996         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16997            c.remove();
16998         });
16999         
17000         this.syncValue();
17001         
17002         this.validate();
17003         
17004         if(this.tickable && !Roo.isTouch){
17005             this.view.refresh();
17006         }
17007     },
17008     
17009     inputEl: function ()
17010     {
17011         if(Roo.isIOS && this.useNativeIOS){
17012             return this.el.select('select.roo-ios-select', true).first();
17013         }
17014         
17015         if(Roo.isTouch && this.mobileTouchView){
17016             return this.el.select('input.form-control',true).first();
17017         }
17018         
17019         if(this.tickable){
17020             return this.searchField;
17021         }
17022         
17023         return this.el.select('input.form-control',true).first();
17024     },
17025     
17026     onTickableFooterButtonClick : function(e, btn, el)
17027     {
17028         e.preventDefault();
17029         
17030         this.lastItem = Roo.apply([], this.item);
17031         
17032         if(btn && btn.name == 'cancel'){
17033             this.tickItems = Roo.apply([], this.item);
17034             this.collapse();
17035             return;
17036         }
17037         
17038         this.clearItem();
17039         
17040         var _this = this;
17041         
17042         Roo.each(this.tickItems, function(o){
17043             _this.addItem(o);
17044         });
17045         
17046         this.collapse();
17047         
17048     },
17049     
17050     validate : function()
17051     {
17052         if(this.getVisibilityEl().hasClass('hidden')){
17053             return true;
17054         }
17055         
17056         var v = this.getRawValue();
17057         
17058         if(this.multiple){
17059             v = this.getValue();
17060         }
17061         
17062         if(this.disabled || this.allowBlank || v.length){
17063             this.markValid();
17064             return true;
17065         }
17066         
17067         this.markInvalid();
17068         return false;
17069     },
17070     
17071     tickableInputEl : function()
17072     {
17073         if(!this.tickable || !this.editable){
17074             return this.inputEl();
17075         }
17076         
17077         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17078     },
17079     
17080     
17081     getAutoCreateTouchView : function()
17082     {
17083         var id = Roo.id();
17084         
17085         var cfg = {
17086             cls: 'form-group' //input-group
17087         };
17088         
17089         var input =  {
17090             tag: 'input',
17091             id : id,
17092             type : this.inputType,
17093             cls : 'form-control x-combo-noedit',
17094             autocomplete: 'new-password',
17095             placeholder : this.placeholder || '',
17096             readonly : true
17097         };
17098         
17099         if (this.name) {
17100             input.name = this.name;
17101         }
17102         
17103         if (this.size) {
17104             input.cls += ' input-' + this.size;
17105         }
17106         
17107         if (this.disabled) {
17108             input.disabled = true;
17109         }
17110         
17111         var inputblock = {
17112             cls : 'roo-combobox-wrap',
17113             cn : [
17114                 input
17115             ]
17116         };
17117         
17118         if(this.before){
17119             inputblock.cls += ' input-group';
17120             
17121             inputblock.cn.unshift({
17122                 tag :'span',
17123                 cls : 'input-group-addon input-group-prepend input-group-text',
17124                 html : this.before
17125             });
17126         }
17127         
17128         if(this.removable && !this.multiple){
17129             inputblock.cls += ' roo-removable';
17130             
17131             inputblock.cn.push({
17132                 tag: 'button',
17133                 html : 'x',
17134                 cls : 'roo-combo-removable-btn close'
17135             });
17136         }
17137
17138         if(this.hasFeedback && !this.allowBlank){
17139             
17140             inputblock.cls += ' has-feedback';
17141             
17142             inputblock.cn.push({
17143                 tag: 'span',
17144                 cls: 'glyphicon form-control-feedback'
17145             });
17146             
17147         }
17148         
17149         if (this.after) {
17150             
17151             inputblock.cls += (this.before) ? '' : ' input-group';
17152             
17153             inputblock.cn.push({
17154                 tag :'span',
17155                 cls : 'input-group-addon input-group-append input-group-text',
17156                 html : this.after
17157             });
17158         }
17159
17160         
17161         var ibwrap = inputblock;
17162         
17163         if(this.multiple){
17164             ibwrap = {
17165                 tag: 'ul',
17166                 cls: 'roo-select2-choices',
17167                 cn:[
17168                     {
17169                         tag: 'li',
17170                         cls: 'roo-select2-search-field',
17171                         cn: [
17172
17173                             inputblock
17174                         ]
17175                     }
17176                 ]
17177             };
17178         
17179             
17180         }
17181         
17182         var combobox = {
17183             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17184             cn: [
17185                 {
17186                     tag: 'input',
17187                     type : 'hidden',
17188                     cls: 'form-hidden-field'
17189                 },
17190                 ibwrap
17191             ]
17192         };
17193         
17194         if(!this.multiple && this.showToggleBtn){
17195             
17196             var caret = {
17197                 cls: 'caret'
17198             };
17199             
17200             if (this.caret != false) {
17201                 caret = {
17202                      tag: 'i',
17203                      cls: 'fa fa-' + this.caret
17204                 };
17205                 
17206             }
17207             
17208             combobox.cn.push({
17209                 tag :'span',
17210                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17211                 cn : [
17212                     Roo.bootstrap.version == 3 ? caret : '',
17213                     {
17214                         tag: 'span',
17215                         cls: 'combobox-clear',
17216                         cn  : [
17217                             {
17218                                 tag : 'i',
17219                                 cls: 'icon-remove'
17220                             }
17221                         ]
17222                     }
17223                 ]
17224
17225             })
17226         }
17227         
17228         if(this.multiple){
17229             combobox.cls += ' roo-select2-container-multi';
17230         }
17231         
17232         var align = this.labelAlign || this.parentLabelAlign();
17233         
17234         if (align ==='left' && this.fieldLabel.length) {
17235
17236             cfg.cn = [
17237                 {
17238                    tag : 'i',
17239                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17240                    tooltip : 'This field is required'
17241                 },
17242                 {
17243                     tag: 'label',
17244                     cls : 'control-label col-form-label',
17245                     html : this.fieldLabel
17246
17247                 },
17248                 {
17249                     cls : 'roo-combobox-wrap ', 
17250                     cn: [
17251                         combobox
17252                     ]
17253                 }
17254             ];
17255             
17256             var labelCfg = cfg.cn[1];
17257             var contentCfg = cfg.cn[2];
17258             
17259
17260             if(this.indicatorpos == 'right'){
17261                 cfg.cn = [
17262                     {
17263                         tag: 'label',
17264                         'for' :  id,
17265                         cls : 'control-label col-form-label',
17266                         cn : [
17267                             {
17268                                 tag : 'span',
17269                                 html : this.fieldLabel
17270                             },
17271                             {
17272                                 tag : 'i',
17273                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17274                                 tooltip : 'This field is required'
17275                             }
17276                         ]
17277                     },
17278                     {
17279                         cls : "roo-combobox-wrap ",
17280                         cn: [
17281                             combobox
17282                         ]
17283                     }
17284
17285                 ];
17286                 
17287                 labelCfg = cfg.cn[0];
17288                 contentCfg = cfg.cn[1];
17289             }
17290             
17291            
17292             
17293             if(this.labelWidth > 12){
17294                 labelCfg.style = "width: " + this.labelWidth + 'px';
17295             }
17296            
17297             if(this.labelWidth < 13 && this.labelmd == 0){
17298                 this.labelmd = this.labelWidth;
17299             }
17300             
17301             if(this.labellg > 0){
17302                 labelCfg.cls += ' col-lg-' + this.labellg;
17303                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17304             }
17305             
17306             if(this.labelmd > 0){
17307                 labelCfg.cls += ' col-md-' + this.labelmd;
17308                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17309             }
17310             
17311             if(this.labelsm > 0){
17312                 labelCfg.cls += ' col-sm-' + this.labelsm;
17313                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17314             }
17315             
17316             if(this.labelxs > 0){
17317                 labelCfg.cls += ' col-xs-' + this.labelxs;
17318                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17319             }
17320                 
17321                 
17322         } else if ( this.fieldLabel.length) {
17323             cfg.cn = [
17324                 {
17325                    tag : 'i',
17326                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17327                    tooltip : 'This field is required'
17328                 },
17329                 {
17330                     tag: 'label',
17331                     cls : 'control-label',
17332                     html : this.fieldLabel
17333
17334                 },
17335                 {
17336                     cls : '', 
17337                     cn: [
17338                         combobox
17339                     ]
17340                 }
17341             ];
17342             
17343             if(this.indicatorpos == 'right'){
17344                 cfg.cn = [
17345                     {
17346                         tag: 'label',
17347                         cls : 'control-label',
17348                         html : this.fieldLabel,
17349                         cn : [
17350                             {
17351                                tag : 'i',
17352                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17353                                tooltip : 'This field is required'
17354                             }
17355                         ]
17356                     },
17357                     {
17358                         cls : '', 
17359                         cn: [
17360                             combobox
17361                         ]
17362                     }
17363                 ];
17364             }
17365         } else {
17366             cfg.cn = combobox;    
17367         }
17368         
17369         
17370         var settings = this;
17371         
17372         ['xs','sm','md','lg'].map(function(size){
17373             if (settings[size]) {
17374                 cfg.cls += ' col-' + size + '-' + settings[size];
17375             }
17376         });
17377         
17378         return cfg;
17379     },
17380     
17381     initTouchView : function()
17382     {
17383         this.renderTouchView();
17384         
17385         this.touchViewEl.on('scroll', function(){
17386             this.el.dom.scrollTop = 0;
17387         }, this);
17388         
17389         this.originalValue = this.getValue();
17390         
17391         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17392         
17393         this.inputEl().on("click", this.showTouchView, this);
17394         if (this.triggerEl) {
17395             this.triggerEl.on("click", this.showTouchView, this);
17396         }
17397         
17398         
17399         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17400         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17401         
17402         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17403         
17404         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17405         this.store.on('load', this.onTouchViewLoad, this);
17406         this.store.on('loadexception', this.onTouchViewLoadException, this);
17407         
17408         if(this.hiddenName){
17409             
17410             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17411             
17412             this.hiddenField.dom.value =
17413                 this.hiddenValue !== undefined ? this.hiddenValue :
17414                 this.value !== undefined ? this.value : '';
17415         
17416             this.el.dom.removeAttribute('name');
17417             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17418         }
17419         
17420         if(this.multiple){
17421             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17422             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17423         }
17424         
17425         if(this.removable && !this.multiple){
17426             var close = this.closeTriggerEl();
17427             if(close){
17428                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17429                 close.on('click', this.removeBtnClick, this, close);
17430             }
17431         }
17432         /*
17433          * fix the bug in Safari iOS8
17434          */
17435         this.inputEl().on("focus", function(e){
17436             document.activeElement.blur();
17437         }, this);
17438         
17439         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17440         
17441         return;
17442         
17443         
17444     },
17445     
17446     renderTouchView : function()
17447     {
17448         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17449         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17450         
17451         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17452         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17453         
17454         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17455         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17456         this.touchViewBodyEl.setStyle('overflow', 'auto');
17457         
17458         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17459         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17460         
17461         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17462         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17463         
17464     },
17465     
17466     showTouchView : function()
17467     {
17468         if(this.disabled){
17469             return;
17470         }
17471         
17472         this.touchViewHeaderEl.hide();
17473
17474         if(this.modalTitle.length){
17475             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17476             this.touchViewHeaderEl.show();
17477         }
17478
17479         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17480         this.touchViewEl.show();
17481
17482         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17483         
17484         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17485         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17486
17487         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17488
17489         if(this.modalTitle.length){
17490             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17491         }
17492         
17493         this.touchViewBodyEl.setHeight(bodyHeight);
17494
17495         if(this.animate){
17496             var _this = this;
17497             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17498         }else{
17499             this.touchViewEl.addClass(['in','show']);
17500         }
17501         
17502         if(this._touchViewMask){
17503             Roo.get(document.body).addClass("x-body-masked");
17504             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17505             this._touchViewMask.setStyle('z-index', 10000);
17506             this._touchViewMask.addClass('show');
17507         }
17508         
17509         this.doTouchViewQuery();
17510         
17511     },
17512     
17513     hideTouchView : function()
17514     {
17515         this.touchViewEl.removeClass(['in','show']);
17516
17517         if(this.animate){
17518             var _this = this;
17519             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17520         }else{
17521             this.touchViewEl.setStyle('display', 'none');
17522         }
17523         
17524         if(this._touchViewMask){
17525             this._touchViewMask.removeClass('show');
17526             Roo.get(document.body).removeClass("x-body-masked");
17527         }
17528     },
17529     
17530     setTouchViewValue : function()
17531     {
17532         if(this.multiple){
17533             this.clearItem();
17534         
17535             var _this = this;
17536
17537             Roo.each(this.tickItems, function(o){
17538                 this.addItem(o);
17539             }, this);
17540         }
17541         
17542         this.hideTouchView();
17543     },
17544     
17545     doTouchViewQuery : function()
17546     {
17547         var qe = {
17548             query: '',
17549             forceAll: true,
17550             combo: this,
17551             cancel:false
17552         };
17553         
17554         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17555             return false;
17556         }
17557         
17558         if(!this.alwaysQuery || this.mode == 'local'){
17559             this.onTouchViewLoad();
17560             return;
17561         }
17562         
17563         this.store.load();
17564     },
17565     
17566     onTouchViewBeforeLoad : function(combo,opts)
17567     {
17568         return;
17569     },
17570
17571     // private
17572     onTouchViewLoad : function()
17573     {
17574         if(this.store.getCount() < 1){
17575             this.onTouchViewEmptyResults();
17576             return;
17577         }
17578         
17579         this.clearTouchView();
17580         
17581         var rawValue = this.getRawValue();
17582         
17583         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17584         
17585         this.tickItems = [];
17586         
17587         this.store.data.each(function(d, rowIndex){
17588             var row = this.touchViewListGroup.createChild(template);
17589             
17590             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17591                 row.addClass(d.data.cls);
17592             }
17593             
17594             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17595                 var cfg = {
17596                     data : d.data,
17597                     html : d.data[this.displayField]
17598                 };
17599                 
17600                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17601                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17602                 }
17603             }
17604             row.removeClass('selected');
17605             if(!this.multiple && this.valueField &&
17606                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17607             {
17608                 // radio buttons..
17609                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17610                 row.addClass('selected');
17611             }
17612             
17613             if(this.multiple && this.valueField &&
17614                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17615             {
17616                 
17617                 // checkboxes...
17618                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17619                 this.tickItems.push(d.data);
17620             }
17621             
17622             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17623             
17624         }, this);
17625         
17626         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17627         
17628         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17629
17630         if(this.modalTitle.length){
17631             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17632         }
17633
17634         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17635         
17636         if(this.mobile_restrict_height && listHeight < bodyHeight){
17637             this.touchViewBodyEl.setHeight(listHeight);
17638         }
17639         
17640         var _this = this;
17641         
17642         if(firstChecked && listHeight > bodyHeight){
17643             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17644         }
17645         
17646     },
17647     
17648     onTouchViewLoadException : function()
17649     {
17650         this.hideTouchView();
17651     },
17652     
17653     onTouchViewEmptyResults : function()
17654     {
17655         this.clearTouchView();
17656         
17657         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17658         
17659         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17660         
17661     },
17662     
17663     clearTouchView : function()
17664     {
17665         this.touchViewListGroup.dom.innerHTML = '';
17666     },
17667     
17668     onTouchViewClick : function(e, el, o)
17669     {
17670         e.preventDefault();
17671         
17672         var row = o.row;
17673         var rowIndex = o.rowIndex;
17674         
17675         var r = this.store.getAt(rowIndex);
17676         
17677         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17678             
17679             if(!this.multiple){
17680                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17681                     c.dom.removeAttribute('checked');
17682                 }, this);
17683
17684                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17685
17686                 this.setFromData(r.data);
17687
17688                 var close = this.closeTriggerEl();
17689
17690                 if(close){
17691                     close.show();
17692                 }
17693
17694                 this.hideTouchView();
17695
17696                 this.fireEvent('select', this, r, rowIndex);
17697
17698                 return;
17699             }
17700
17701             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17702                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17703                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17704                 return;
17705             }
17706
17707             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17708             this.addItem(r.data);
17709             this.tickItems.push(r.data);
17710         }
17711     },
17712     
17713     getAutoCreateNativeIOS : function()
17714     {
17715         var cfg = {
17716             cls: 'form-group' //input-group,
17717         };
17718         
17719         var combobox =  {
17720             tag: 'select',
17721             cls : 'roo-ios-select'
17722         };
17723         
17724         if (this.name) {
17725             combobox.name = this.name;
17726         }
17727         
17728         if (this.disabled) {
17729             combobox.disabled = true;
17730         }
17731         
17732         var settings = this;
17733         
17734         ['xs','sm','md','lg'].map(function(size){
17735             if (settings[size]) {
17736                 cfg.cls += ' col-' + size + '-' + settings[size];
17737             }
17738         });
17739         
17740         cfg.cn = combobox;
17741         
17742         return cfg;
17743         
17744     },
17745     
17746     initIOSView : function()
17747     {
17748         this.store.on('load', this.onIOSViewLoad, this);
17749         
17750         return;
17751     },
17752     
17753     onIOSViewLoad : function()
17754     {
17755         if(this.store.getCount() < 1){
17756             return;
17757         }
17758         
17759         this.clearIOSView();
17760         
17761         if(this.allowBlank) {
17762             
17763             var default_text = '-- SELECT --';
17764             
17765             if(this.placeholder.length){
17766                 default_text = this.placeholder;
17767             }
17768             
17769             if(this.emptyTitle.length){
17770                 default_text += ' - ' + this.emptyTitle + ' -';
17771             }
17772             
17773             var opt = this.inputEl().createChild({
17774                 tag: 'option',
17775                 value : 0,
17776                 html : default_text
17777             });
17778             
17779             var o = {};
17780             o[this.valueField] = 0;
17781             o[this.displayField] = default_text;
17782             
17783             this.ios_options.push({
17784                 data : o,
17785                 el : opt
17786             });
17787             
17788         }
17789         
17790         this.store.data.each(function(d, rowIndex){
17791             
17792             var html = '';
17793             
17794             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17795                 html = d.data[this.displayField];
17796             }
17797             
17798             var value = '';
17799             
17800             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17801                 value = d.data[this.valueField];
17802             }
17803             
17804             var option = {
17805                 tag: 'option',
17806                 value : value,
17807                 html : html
17808             };
17809             
17810             if(this.value == d.data[this.valueField]){
17811                 option['selected'] = true;
17812             }
17813             
17814             var opt = this.inputEl().createChild(option);
17815             
17816             this.ios_options.push({
17817                 data : d.data,
17818                 el : opt
17819             });
17820             
17821         }, this);
17822         
17823         this.inputEl().on('change', function(){
17824            this.fireEvent('select', this);
17825         }, this);
17826         
17827     },
17828     
17829     clearIOSView: function()
17830     {
17831         this.inputEl().dom.innerHTML = '';
17832         
17833         this.ios_options = [];
17834     },
17835     
17836     setIOSValue: function(v)
17837     {
17838         this.value = v;
17839         
17840         if(!this.ios_options){
17841             return;
17842         }
17843         
17844         Roo.each(this.ios_options, function(opts){
17845            
17846            opts.el.dom.removeAttribute('selected');
17847            
17848            if(opts.data[this.valueField] != v){
17849                return;
17850            }
17851            
17852            opts.el.dom.setAttribute('selected', true);
17853            
17854         }, this);
17855     }
17856
17857     /** 
17858     * @cfg {Boolean} grow 
17859     * @hide 
17860     */
17861     /** 
17862     * @cfg {Number} growMin 
17863     * @hide 
17864     */
17865     /** 
17866     * @cfg {Number} growMax 
17867     * @hide 
17868     */
17869     /**
17870      * @hide
17871      * @method autoSize
17872      */
17873 });
17874
17875 Roo.apply(Roo.bootstrap.ComboBox,  {
17876     
17877     header : {
17878         tag: 'div',
17879         cls: 'modal-header',
17880         cn: [
17881             {
17882                 tag: 'h4',
17883                 cls: 'modal-title'
17884             }
17885         ]
17886     },
17887     
17888     body : {
17889         tag: 'div',
17890         cls: 'modal-body',
17891         cn: [
17892             {
17893                 tag: 'ul',
17894                 cls: 'list-group'
17895             }
17896         ]
17897     },
17898     
17899     listItemRadio : {
17900         tag: 'li',
17901         cls: 'list-group-item',
17902         cn: [
17903             {
17904                 tag: 'span',
17905                 cls: 'roo-combobox-list-group-item-value'
17906             },
17907             {
17908                 tag: 'div',
17909                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17910                 cn: [
17911                     {
17912                         tag: 'input',
17913                         type: 'radio'
17914                     },
17915                     {
17916                         tag: 'label'
17917                     }
17918                 ]
17919             }
17920         ]
17921     },
17922     
17923     listItemCheckbox : {
17924         tag: 'li',
17925         cls: 'list-group-item',
17926         cn: [
17927             {
17928                 tag: 'span',
17929                 cls: 'roo-combobox-list-group-item-value'
17930             },
17931             {
17932                 tag: 'div',
17933                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17934                 cn: [
17935                     {
17936                         tag: 'input',
17937                         type: 'checkbox'
17938                     },
17939                     {
17940                         tag: 'label'
17941                     }
17942                 ]
17943             }
17944         ]
17945     },
17946     
17947     emptyResult : {
17948         tag: 'div',
17949         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17950     },
17951     
17952     footer : {
17953         tag: 'div',
17954         cls: 'modal-footer',
17955         cn: [
17956             {
17957                 tag: 'div',
17958                 cls: 'row',
17959                 cn: [
17960                     {
17961                         tag: 'div',
17962                         cls: 'col-xs-6 text-left',
17963                         cn: {
17964                             tag: 'button',
17965                             cls: 'btn btn-danger roo-touch-view-cancel',
17966                             html: 'Cancel'
17967                         }
17968                     },
17969                     {
17970                         tag: 'div',
17971                         cls: 'col-xs-6 text-right',
17972                         cn: {
17973                             tag: 'button',
17974                             cls: 'btn btn-success roo-touch-view-ok',
17975                             html: 'OK'
17976                         }
17977                     }
17978                 ]
17979             }
17980         ]
17981         
17982     }
17983 });
17984
17985 Roo.apply(Roo.bootstrap.ComboBox,  {
17986     
17987     touchViewTemplate : {
17988         tag: 'div',
17989         cls: 'modal fade roo-combobox-touch-view',
17990         cn: [
17991             {
17992                 tag: 'div',
17993                 cls: 'modal-dialog',
17994                 style : 'position:fixed', // we have to fix position....
17995                 cn: [
17996                     {
17997                         tag: 'div',
17998                         cls: 'modal-content',
17999                         cn: [
18000                             Roo.bootstrap.ComboBox.header,
18001                             Roo.bootstrap.ComboBox.body,
18002                             Roo.bootstrap.ComboBox.footer
18003                         ]
18004                     }
18005                 ]
18006             }
18007         ]
18008     }
18009 });/*
18010  * Based on:
18011  * Ext JS Library 1.1.1
18012  * Copyright(c) 2006-2007, Ext JS, LLC.
18013  *
18014  * Originally Released Under LGPL - original licence link has changed is not relivant.
18015  *
18016  * Fork - LGPL
18017  * <script type="text/javascript">
18018  */
18019
18020 /**
18021  * @class Roo.View
18022  * @extends Roo.util.Observable
18023  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18024  * This class also supports single and multi selection modes. <br>
18025  * Create a data model bound view:
18026  <pre><code>
18027  var store = new Roo.data.Store(...);
18028
18029  var view = new Roo.View({
18030     el : "my-element",
18031     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18032  
18033     singleSelect: true,
18034     selectedClass: "ydataview-selected",
18035     store: store
18036  });
18037
18038  // listen for node click?
18039  view.on("click", function(vw, index, node, e){
18040  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18041  });
18042
18043  // load XML data
18044  dataModel.load("foobar.xml");
18045  </code></pre>
18046  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18047  * <br><br>
18048  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18049  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18050  * 
18051  * Note: old style constructor is still suported (container, template, config)
18052  * 
18053  * @constructor
18054  * Create a new View
18055  * @param {Object} config The config object
18056  * 
18057  */
18058 Roo.View = function(config, depreciated_tpl, depreciated_config){
18059     
18060     this.parent = false;
18061     
18062     if (typeof(depreciated_tpl) == 'undefined') {
18063         // new way.. - universal constructor.
18064         Roo.apply(this, config);
18065         this.el  = Roo.get(this.el);
18066     } else {
18067         // old format..
18068         this.el  = Roo.get(config);
18069         this.tpl = depreciated_tpl;
18070         Roo.apply(this, depreciated_config);
18071     }
18072     this.wrapEl  = this.el.wrap().wrap();
18073     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18074     
18075     
18076     if(typeof(this.tpl) == "string"){
18077         this.tpl = new Roo.Template(this.tpl);
18078     } else {
18079         // support xtype ctors..
18080         this.tpl = new Roo.factory(this.tpl, Roo);
18081     }
18082     
18083     
18084     this.tpl.compile();
18085     
18086     /** @private */
18087     this.addEvents({
18088         /**
18089          * @event beforeclick
18090          * Fires before a click is processed. Returns false to cancel the default action.
18091          * @param {Roo.View} this
18092          * @param {Number} index The index of the target node
18093          * @param {HTMLElement} node The target node
18094          * @param {Roo.EventObject} e The raw event object
18095          */
18096             "beforeclick" : true,
18097         /**
18098          * @event click
18099          * Fires when a template node is clicked.
18100          * @param {Roo.View} this
18101          * @param {Number} index The index of the target node
18102          * @param {HTMLElement} node The target node
18103          * @param {Roo.EventObject} e The raw event object
18104          */
18105             "click" : true,
18106         /**
18107          * @event dblclick
18108          * Fires when a template node is double clicked.
18109          * @param {Roo.View} this
18110          * @param {Number} index The index of the target node
18111          * @param {HTMLElement} node The target node
18112          * @param {Roo.EventObject} e The raw event object
18113          */
18114             "dblclick" : true,
18115         /**
18116          * @event contextmenu
18117          * Fires when a template node is right clicked.
18118          * @param {Roo.View} this
18119          * @param {Number} index The index of the target node
18120          * @param {HTMLElement} node The target node
18121          * @param {Roo.EventObject} e The raw event object
18122          */
18123             "contextmenu" : true,
18124         /**
18125          * @event selectionchange
18126          * Fires when the selected nodes change.
18127          * @param {Roo.View} this
18128          * @param {Array} selections Array of the selected nodes
18129          */
18130             "selectionchange" : true,
18131     
18132         /**
18133          * @event beforeselect
18134          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18135          * @param {Roo.View} this
18136          * @param {HTMLElement} node The node to be selected
18137          * @param {Array} selections Array of currently selected nodes
18138          */
18139             "beforeselect" : true,
18140         /**
18141          * @event preparedata
18142          * Fires on every row to render, to allow you to change the data.
18143          * @param {Roo.View} this
18144          * @param {Object} data to be rendered (change this)
18145          */
18146           "preparedata" : true
18147           
18148           
18149         });
18150
18151
18152
18153     this.el.on({
18154         "click": this.onClick,
18155         "dblclick": this.onDblClick,
18156         "contextmenu": this.onContextMenu,
18157         scope:this
18158     });
18159
18160     this.selections = [];
18161     this.nodes = [];
18162     this.cmp = new Roo.CompositeElementLite([]);
18163     if(this.store){
18164         this.store = Roo.factory(this.store, Roo.data);
18165         this.setStore(this.store, true);
18166     }
18167     
18168     if ( this.footer && this.footer.xtype) {
18169            
18170          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18171         
18172         this.footer.dataSource = this.store;
18173         this.footer.container = fctr;
18174         this.footer = Roo.factory(this.footer, Roo);
18175         fctr.insertFirst(this.el);
18176         
18177         // this is a bit insane - as the paging toolbar seems to detach the el..
18178 //        dom.parentNode.parentNode.parentNode
18179          // they get detached?
18180     }
18181     
18182     
18183     Roo.View.superclass.constructor.call(this);
18184     
18185     
18186 };
18187
18188 Roo.extend(Roo.View, Roo.util.Observable, {
18189     
18190      /**
18191      * @cfg {Roo.data.Store} store Data store to load data from.
18192      */
18193     store : false,
18194     
18195     /**
18196      * @cfg {String|Roo.Element} el The container element.
18197      */
18198     el : '',
18199     
18200     /**
18201      * @cfg {String|Roo.Template} tpl The template used by this View 
18202      */
18203     tpl : false,
18204     /**
18205      * @cfg {String} dataName the named area of the template to use as the data area
18206      *                          Works with domtemplates roo-name="name"
18207      */
18208     dataName: false,
18209     /**
18210      * @cfg {String} selectedClass The css class to add to selected nodes
18211      */
18212     selectedClass : "x-view-selected",
18213      /**
18214      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18215      */
18216     emptyText : "",
18217     
18218     /**
18219      * @cfg {String} text to display on mask (default Loading)
18220      */
18221     mask : false,
18222     /**
18223      * @cfg {Boolean} multiSelect Allow multiple selection
18224      */
18225     multiSelect : false,
18226     /**
18227      * @cfg {Boolean} singleSelect Allow single selection
18228      */
18229     singleSelect:  false,
18230     
18231     /**
18232      * @cfg {Boolean} toggleSelect - selecting 
18233      */
18234     toggleSelect : false,
18235     
18236     /**
18237      * @cfg {Boolean} tickable - selecting 
18238      */
18239     tickable : false,
18240     
18241     /**
18242      * Returns the element this view is bound to.
18243      * @return {Roo.Element}
18244      */
18245     getEl : function(){
18246         return this.wrapEl;
18247     },
18248     
18249     
18250
18251     /**
18252      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18253      */
18254     refresh : function(){
18255         //Roo.log('refresh');
18256         var t = this.tpl;
18257         
18258         // if we are using something like 'domtemplate', then
18259         // the what gets used is:
18260         // t.applySubtemplate(NAME, data, wrapping data..)
18261         // the outer template then get' applied with
18262         //     the store 'extra data'
18263         // and the body get's added to the
18264         //      roo-name="data" node?
18265         //      <span class='roo-tpl-{name}'></span> ?????
18266         
18267         
18268         
18269         this.clearSelections();
18270         this.el.update("");
18271         var html = [];
18272         var records = this.store.getRange();
18273         if(records.length < 1) {
18274             
18275             // is this valid??  = should it render a template??
18276             
18277             this.el.update(this.emptyText);
18278             return;
18279         }
18280         var el = this.el;
18281         if (this.dataName) {
18282             this.el.update(t.apply(this.store.meta)); //????
18283             el = this.el.child('.roo-tpl-' + this.dataName);
18284         }
18285         
18286         for(var i = 0, len = records.length; i < len; i++){
18287             var data = this.prepareData(records[i].data, i, records[i]);
18288             this.fireEvent("preparedata", this, data, i, records[i]);
18289             
18290             var d = Roo.apply({}, data);
18291             
18292             if(this.tickable){
18293                 Roo.apply(d, {'roo-id' : Roo.id()});
18294                 
18295                 var _this = this;
18296             
18297                 Roo.each(this.parent.item, function(item){
18298                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18299                         return;
18300                     }
18301                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18302                 });
18303             }
18304             
18305             html[html.length] = Roo.util.Format.trim(
18306                 this.dataName ?
18307                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18308                     t.apply(d)
18309             );
18310         }
18311         
18312         
18313         
18314         el.update(html.join(""));
18315         this.nodes = el.dom.childNodes;
18316         this.updateIndexes(0);
18317     },
18318     
18319
18320     /**
18321      * Function to override to reformat the data that is sent to
18322      * the template for each node.
18323      * DEPRICATED - use the preparedata event handler.
18324      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18325      * a JSON object for an UpdateManager bound view).
18326      */
18327     prepareData : function(data, index, record)
18328     {
18329         this.fireEvent("preparedata", this, data, index, record);
18330         return data;
18331     },
18332
18333     onUpdate : function(ds, record){
18334         // Roo.log('on update');   
18335         this.clearSelections();
18336         var index = this.store.indexOf(record);
18337         var n = this.nodes[index];
18338         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18339         n.parentNode.removeChild(n);
18340         this.updateIndexes(index, index);
18341     },
18342
18343     
18344     
18345 // --------- FIXME     
18346     onAdd : function(ds, records, index)
18347     {
18348         //Roo.log(['on Add', ds, records, index] );        
18349         this.clearSelections();
18350         if(this.nodes.length == 0){
18351             this.refresh();
18352             return;
18353         }
18354         var n = this.nodes[index];
18355         for(var i = 0, len = records.length; i < len; i++){
18356             var d = this.prepareData(records[i].data, i, records[i]);
18357             if(n){
18358                 this.tpl.insertBefore(n, d);
18359             }else{
18360                 
18361                 this.tpl.append(this.el, d);
18362             }
18363         }
18364         this.updateIndexes(index);
18365     },
18366
18367     onRemove : function(ds, record, index){
18368        // Roo.log('onRemove');
18369         this.clearSelections();
18370         var el = this.dataName  ?
18371             this.el.child('.roo-tpl-' + this.dataName) :
18372             this.el; 
18373         
18374         el.dom.removeChild(this.nodes[index]);
18375         this.updateIndexes(index);
18376     },
18377
18378     /**
18379      * Refresh an individual node.
18380      * @param {Number} index
18381      */
18382     refreshNode : function(index){
18383         this.onUpdate(this.store, this.store.getAt(index));
18384     },
18385
18386     updateIndexes : function(startIndex, endIndex){
18387         var ns = this.nodes;
18388         startIndex = startIndex || 0;
18389         endIndex = endIndex || ns.length - 1;
18390         for(var i = startIndex; i <= endIndex; i++){
18391             ns[i].nodeIndex = i;
18392         }
18393     },
18394
18395     /**
18396      * Changes the data store this view uses and refresh the view.
18397      * @param {Store} store
18398      */
18399     setStore : function(store, initial){
18400         if(!initial && this.store){
18401             this.store.un("datachanged", this.refresh);
18402             this.store.un("add", this.onAdd);
18403             this.store.un("remove", this.onRemove);
18404             this.store.un("update", this.onUpdate);
18405             this.store.un("clear", this.refresh);
18406             this.store.un("beforeload", this.onBeforeLoad);
18407             this.store.un("load", this.onLoad);
18408             this.store.un("loadexception", this.onLoad);
18409         }
18410         if(store){
18411           
18412             store.on("datachanged", this.refresh, this);
18413             store.on("add", this.onAdd, this);
18414             store.on("remove", this.onRemove, this);
18415             store.on("update", this.onUpdate, this);
18416             store.on("clear", this.refresh, this);
18417             store.on("beforeload", this.onBeforeLoad, this);
18418             store.on("load", this.onLoad, this);
18419             store.on("loadexception", this.onLoad, this);
18420         }
18421         
18422         if(store){
18423             this.refresh();
18424         }
18425     },
18426     /**
18427      * onbeforeLoad - masks the loading area.
18428      *
18429      */
18430     onBeforeLoad : function(store,opts)
18431     {
18432          //Roo.log('onBeforeLoad');   
18433         if (!opts.add) {
18434             this.el.update("");
18435         }
18436         this.el.mask(this.mask ? this.mask : "Loading" ); 
18437     },
18438     onLoad : function ()
18439     {
18440         this.el.unmask();
18441     },
18442     
18443
18444     /**
18445      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18446      * @param {HTMLElement} node
18447      * @return {HTMLElement} The template node
18448      */
18449     findItemFromChild : function(node){
18450         var el = this.dataName  ?
18451             this.el.child('.roo-tpl-' + this.dataName,true) :
18452             this.el.dom; 
18453         
18454         if(!node || node.parentNode == el){
18455                     return node;
18456             }
18457             var p = node.parentNode;
18458             while(p && p != el){
18459             if(p.parentNode == el){
18460                 return p;
18461             }
18462             p = p.parentNode;
18463         }
18464             return null;
18465     },
18466
18467     /** @ignore */
18468     onClick : function(e){
18469         var item = this.findItemFromChild(e.getTarget());
18470         if(item){
18471             var index = this.indexOf(item);
18472             if(this.onItemClick(item, index, e) !== false){
18473                 this.fireEvent("click", this, index, item, e);
18474             }
18475         }else{
18476             this.clearSelections();
18477         }
18478     },
18479
18480     /** @ignore */
18481     onContextMenu : function(e){
18482         var item = this.findItemFromChild(e.getTarget());
18483         if(item){
18484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18485         }
18486     },
18487
18488     /** @ignore */
18489     onDblClick : function(e){
18490         var item = this.findItemFromChild(e.getTarget());
18491         if(item){
18492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18493         }
18494     },
18495
18496     onItemClick : function(item, index, e)
18497     {
18498         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18499             return false;
18500         }
18501         if (this.toggleSelect) {
18502             var m = this.isSelected(item) ? 'unselect' : 'select';
18503             //Roo.log(m);
18504             var _t = this;
18505             _t[m](item, true, false);
18506             return true;
18507         }
18508         if(this.multiSelect || this.singleSelect){
18509             if(this.multiSelect && e.shiftKey && this.lastSelection){
18510                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18511             }else{
18512                 this.select(item, this.multiSelect && e.ctrlKey);
18513                 this.lastSelection = item;
18514             }
18515             
18516             if(!this.tickable){
18517                 e.preventDefault();
18518             }
18519             
18520         }
18521         return true;
18522     },
18523
18524     /**
18525      * Get the number of selected nodes.
18526      * @return {Number}
18527      */
18528     getSelectionCount : function(){
18529         return this.selections.length;
18530     },
18531
18532     /**
18533      * Get the currently selected nodes.
18534      * @return {Array} An array of HTMLElements
18535      */
18536     getSelectedNodes : function(){
18537         return this.selections;
18538     },
18539
18540     /**
18541      * Get the indexes of the selected nodes.
18542      * @return {Array}
18543      */
18544     getSelectedIndexes : function(){
18545         var indexes = [], s = this.selections;
18546         for(var i = 0, len = s.length; i < len; i++){
18547             indexes.push(s[i].nodeIndex);
18548         }
18549         return indexes;
18550     },
18551
18552     /**
18553      * Clear all selections
18554      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18555      */
18556     clearSelections : function(suppressEvent){
18557         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18558             this.cmp.elements = this.selections;
18559             this.cmp.removeClass(this.selectedClass);
18560             this.selections = [];
18561             if(!suppressEvent){
18562                 this.fireEvent("selectionchange", this, this.selections);
18563             }
18564         }
18565     },
18566
18567     /**
18568      * Returns true if the passed node is selected
18569      * @param {HTMLElement/Number} node The node or node index
18570      * @return {Boolean}
18571      */
18572     isSelected : function(node){
18573         var s = this.selections;
18574         if(s.length < 1){
18575             return false;
18576         }
18577         node = this.getNode(node);
18578         return s.indexOf(node) !== -1;
18579     },
18580
18581     /**
18582      * Selects nodes.
18583      * @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
18584      * @param {Boolean} keepExisting (optional) true to keep existing selections
18585      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18586      */
18587     select : function(nodeInfo, keepExisting, suppressEvent){
18588         if(nodeInfo instanceof Array){
18589             if(!keepExisting){
18590                 this.clearSelections(true);
18591             }
18592             for(var i = 0, len = nodeInfo.length; i < len; i++){
18593                 this.select(nodeInfo[i], true, true);
18594             }
18595             return;
18596         } 
18597         var node = this.getNode(nodeInfo);
18598         if(!node || this.isSelected(node)){
18599             return; // already selected.
18600         }
18601         if(!keepExisting){
18602             this.clearSelections(true);
18603         }
18604         
18605         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18606             Roo.fly(node).addClass(this.selectedClass);
18607             this.selections.push(node);
18608             if(!suppressEvent){
18609                 this.fireEvent("selectionchange", this, this.selections);
18610             }
18611         }
18612         
18613         
18614     },
18615       /**
18616      * Unselects nodes.
18617      * @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
18618      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18619      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18620      */
18621     unselect : function(nodeInfo, keepExisting, suppressEvent)
18622     {
18623         if(nodeInfo instanceof Array){
18624             Roo.each(this.selections, function(s) {
18625                 this.unselect(s, nodeInfo);
18626             }, this);
18627             return;
18628         }
18629         var node = this.getNode(nodeInfo);
18630         if(!node || !this.isSelected(node)){
18631             //Roo.log("not selected");
18632             return; // not selected.
18633         }
18634         // fireevent???
18635         var ns = [];
18636         Roo.each(this.selections, function(s) {
18637             if (s == node ) {
18638                 Roo.fly(node).removeClass(this.selectedClass);
18639
18640                 return;
18641             }
18642             ns.push(s);
18643         },this);
18644         
18645         this.selections= ns;
18646         this.fireEvent("selectionchange", this, this.selections);
18647     },
18648
18649     /**
18650      * Gets a template node.
18651      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18652      * @return {HTMLElement} The node or null if it wasn't found
18653      */
18654     getNode : function(nodeInfo){
18655         if(typeof nodeInfo == "string"){
18656             return document.getElementById(nodeInfo);
18657         }else if(typeof nodeInfo == "number"){
18658             return this.nodes[nodeInfo];
18659         }
18660         return nodeInfo;
18661     },
18662
18663     /**
18664      * Gets a range template nodes.
18665      * @param {Number} startIndex
18666      * @param {Number} endIndex
18667      * @return {Array} An array of nodes
18668      */
18669     getNodes : function(start, end){
18670         var ns = this.nodes;
18671         start = start || 0;
18672         end = typeof end == "undefined" ? ns.length - 1 : end;
18673         var nodes = [];
18674         if(start <= end){
18675             for(var i = start; i <= end; i++){
18676                 nodes.push(ns[i]);
18677             }
18678         } else{
18679             for(var i = start; i >= end; i--){
18680                 nodes.push(ns[i]);
18681             }
18682         }
18683         return nodes;
18684     },
18685
18686     /**
18687      * Finds the index of the passed node
18688      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18689      * @return {Number} The index of the node or -1
18690      */
18691     indexOf : function(node){
18692         node = this.getNode(node);
18693         if(typeof node.nodeIndex == "number"){
18694             return node.nodeIndex;
18695         }
18696         var ns = this.nodes;
18697         for(var i = 0, len = ns.length; i < len; i++){
18698             if(ns[i] == node){
18699                 return i;
18700             }
18701         }
18702         return -1;
18703     }
18704 });
18705 /*
18706  * - LGPL
18707  *
18708  * based on jquery fullcalendar
18709  * 
18710  */
18711
18712 Roo.bootstrap = Roo.bootstrap || {};
18713 /**
18714  * @class Roo.bootstrap.Calendar
18715  * @extends Roo.bootstrap.Component
18716  * Bootstrap Calendar class
18717  * @cfg {Boolean} loadMask (true|false) default false
18718  * @cfg {Object} header generate the user specific header of the calendar, default false
18719
18720  * @constructor
18721  * Create a new Container
18722  * @param {Object} config The config object
18723  */
18724
18725
18726
18727 Roo.bootstrap.Calendar = function(config){
18728     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18729      this.addEvents({
18730         /**
18731              * @event select
18732              * Fires when a date is selected
18733              * @param {DatePicker} this
18734              * @param {Date} date The selected date
18735              */
18736         'select': true,
18737         /**
18738              * @event monthchange
18739              * Fires when the displayed month changes 
18740              * @param {DatePicker} this
18741              * @param {Date} date The selected month
18742              */
18743         'monthchange': true,
18744         /**
18745              * @event evententer
18746              * Fires when mouse over an event
18747              * @param {Calendar} this
18748              * @param {event} Event
18749              */
18750         'evententer': true,
18751         /**
18752              * @event eventleave
18753              * Fires when the mouse leaves an
18754              * @param {Calendar} this
18755              * @param {event}
18756              */
18757         'eventleave': true,
18758         /**
18759              * @event eventclick
18760              * Fires when the mouse click an
18761              * @param {Calendar} this
18762              * @param {event}
18763              */
18764         'eventclick': true
18765         
18766     });
18767
18768 };
18769
18770 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18771     
18772      /**
18773      * @cfg {Number} startDay
18774      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18775      */
18776     startDay : 0,
18777     
18778     loadMask : false,
18779     
18780     header : false,
18781       
18782     getAutoCreate : function(){
18783         
18784         
18785         var fc_button = function(name, corner, style, content ) {
18786             return Roo.apply({},{
18787                 tag : 'span',
18788                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18789                          (corner.length ?
18790                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18791                             ''
18792                         ),
18793                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18794                 unselectable: 'on'
18795             });
18796         };
18797         
18798         var header = {};
18799         
18800         if(!this.header){
18801             header = {
18802                 tag : 'table',
18803                 cls : 'fc-header',
18804                 style : 'width:100%',
18805                 cn : [
18806                     {
18807                         tag: 'tr',
18808                         cn : [
18809                             {
18810                                 tag : 'td',
18811                                 cls : 'fc-header-left',
18812                                 cn : [
18813                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18814                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18815                                     { tag: 'span', cls: 'fc-header-space' },
18816                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18817
18818
18819                                 ]
18820                             },
18821
18822                             {
18823                                 tag : 'td',
18824                                 cls : 'fc-header-center',
18825                                 cn : [
18826                                     {
18827                                         tag: 'span',
18828                                         cls: 'fc-header-title',
18829                                         cn : {
18830                                             tag: 'H2',
18831                                             html : 'month / year'
18832                                         }
18833                                     }
18834
18835                                 ]
18836                             },
18837                             {
18838                                 tag : 'td',
18839                                 cls : 'fc-header-right',
18840                                 cn : [
18841                               /*      fc_button('month', 'left', '', 'month' ),
18842                                     fc_button('week', '', '', 'week' ),
18843                                     fc_button('day', 'right', '', 'day' )
18844                                 */    
18845
18846                                 ]
18847                             }
18848
18849                         ]
18850                     }
18851                 ]
18852             };
18853         }
18854         
18855         header = this.header;
18856         
18857        
18858         var cal_heads = function() {
18859             var ret = [];
18860             // fixme - handle this.
18861             
18862             for (var i =0; i < Date.dayNames.length; i++) {
18863                 var d = Date.dayNames[i];
18864                 ret.push({
18865                     tag: 'th',
18866                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18867                     html : d.substring(0,3)
18868                 });
18869                 
18870             }
18871             ret[0].cls += ' fc-first';
18872             ret[6].cls += ' fc-last';
18873             return ret;
18874         };
18875         var cal_cell = function(n) {
18876             return  {
18877                 tag: 'td',
18878                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18879                 cn : [
18880                     {
18881                         cn : [
18882                             {
18883                                 cls: 'fc-day-number',
18884                                 html: 'D'
18885                             },
18886                             {
18887                                 cls: 'fc-day-content',
18888                              
18889                                 cn : [
18890                                      {
18891                                         style: 'position: relative;' // height: 17px;
18892                                     }
18893                                 ]
18894                             }
18895                             
18896                             
18897                         ]
18898                     }
18899                 ]
18900                 
18901             }
18902         };
18903         var cal_rows = function() {
18904             
18905             var ret = [];
18906             for (var r = 0; r < 6; r++) {
18907                 var row= {
18908                     tag : 'tr',
18909                     cls : 'fc-week',
18910                     cn : []
18911                 };
18912                 
18913                 for (var i =0; i < Date.dayNames.length; i++) {
18914                     var d = Date.dayNames[i];
18915                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18916
18917                 }
18918                 row.cn[0].cls+=' fc-first';
18919                 row.cn[0].cn[0].style = 'min-height:90px';
18920                 row.cn[6].cls+=' fc-last';
18921                 ret.push(row);
18922                 
18923             }
18924             ret[0].cls += ' fc-first';
18925             ret[4].cls += ' fc-prev-last';
18926             ret[5].cls += ' fc-last';
18927             return ret;
18928             
18929         };
18930         
18931         var cal_table = {
18932             tag: 'table',
18933             cls: 'fc-border-separate',
18934             style : 'width:100%',
18935             cellspacing  : 0,
18936             cn : [
18937                 { 
18938                     tag: 'thead',
18939                     cn : [
18940                         { 
18941                             tag: 'tr',
18942                             cls : 'fc-first fc-last',
18943                             cn : cal_heads()
18944                         }
18945                     ]
18946                 },
18947                 { 
18948                     tag: 'tbody',
18949                     cn : cal_rows()
18950                 }
18951                   
18952             ]
18953         };
18954          
18955          var cfg = {
18956             cls : 'fc fc-ltr',
18957             cn : [
18958                 header,
18959                 {
18960                     cls : 'fc-content',
18961                     style : "position: relative;",
18962                     cn : [
18963                         {
18964                             cls : 'fc-view fc-view-month fc-grid',
18965                             style : 'position: relative',
18966                             unselectable : 'on',
18967                             cn : [
18968                                 {
18969                                     cls : 'fc-event-container',
18970                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18971                                 },
18972                                 cal_table
18973                             ]
18974                         }
18975                     ]
18976     
18977                 }
18978            ] 
18979             
18980         };
18981         
18982          
18983         
18984         return cfg;
18985     },
18986     
18987     
18988     initEvents : function()
18989     {
18990         if(!this.store){
18991             throw "can not find store for calendar";
18992         }
18993         
18994         var mark = {
18995             tag: "div",
18996             cls:"x-dlg-mask",
18997             style: "text-align:center",
18998             cn: [
18999                 {
19000                     tag: "div",
19001                     style: "background-color:white;width:50%;margin:250 auto",
19002                     cn: [
19003                         {
19004                             tag: "img",
19005                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19006                         },
19007                         {
19008                             tag: "span",
19009                             html: "Loading"
19010                         }
19011                         
19012                     ]
19013                 }
19014             ]
19015         };
19016         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19017         
19018         var size = this.el.select('.fc-content', true).first().getSize();
19019         this.maskEl.setSize(size.width, size.height);
19020         this.maskEl.enableDisplayMode("block");
19021         if(!this.loadMask){
19022             this.maskEl.hide();
19023         }
19024         
19025         this.store = Roo.factory(this.store, Roo.data);
19026         this.store.on('load', this.onLoad, this);
19027         this.store.on('beforeload', this.onBeforeLoad, this);
19028         
19029         this.resize();
19030         
19031         this.cells = this.el.select('.fc-day',true);
19032         //Roo.log(this.cells);
19033         this.textNodes = this.el.query('.fc-day-number');
19034         this.cells.addClassOnOver('fc-state-hover');
19035         
19036         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19037         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19038         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19039         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19040         
19041         this.on('monthchange', this.onMonthChange, this);
19042         
19043         this.update(new Date().clearTime());
19044     },
19045     
19046     resize : function() {
19047         var sz  = this.el.getSize();
19048         
19049         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19050         this.el.select('.fc-day-content div',true).setHeight(34);
19051     },
19052     
19053     
19054     // private
19055     showPrevMonth : function(e){
19056         this.update(this.activeDate.add("mo", -1));
19057     },
19058     showToday : function(e){
19059         this.update(new Date().clearTime());
19060     },
19061     // private
19062     showNextMonth : function(e){
19063         this.update(this.activeDate.add("mo", 1));
19064     },
19065
19066     // private
19067     showPrevYear : function(){
19068         this.update(this.activeDate.add("y", -1));
19069     },
19070
19071     // private
19072     showNextYear : function(){
19073         this.update(this.activeDate.add("y", 1));
19074     },
19075
19076     
19077    // private
19078     update : function(date)
19079     {
19080         var vd = this.activeDate;
19081         this.activeDate = date;
19082 //        if(vd && this.el){
19083 //            var t = date.getTime();
19084 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19085 //                Roo.log('using add remove');
19086 //                
19087 //                this.fireEvent('monthchange', this, date);
19088 //                
19089 //                this.cells.removeClass("fc-state-highlight");
19090 //                this.cells.each(function(c){
19091 //                   if(c.dateValue == t){
19092 //                       c.addClass("fc-state-highlight");
19093 //                       setTimeout(function(){
19094 //                            try{c.dom.firstChild.focus();}catch(e){}
19095 //                       }, 50);
19096 //                       return false;
19097 //                   }
19098 //                   return true;
19099 //                });
19100 //                return;
19101 //            }
19102 //        }
19103         
19104         var days = date.getDaysInMonth();
19105         
19106         var firstOfMonth = date.getFirstDateOfMonth();
19107         var startingPos = firstOfMonth.getDay()-this.startDay;
19108         
19109         if(startingPos < this.startDay){
19110             startingPos += 7;
19111         }
19112         
19113         var pm = date.add(Date.MONTH, -1);
19114         var prevStart = pm.getDaysInMonth()-startingPos;
19115 //        
19116         this.cells = this.el.select('.fc-day',true);
19117         this.textNodes = this.el.query('.fc-day-number');
19118         this.cells.addClassOnOver('fc-state-hover');
19119         
19120         var cells = this.cells.elements;
19121         var textEls = this.textNodes;
19122         
19123         Roo.each(cells, function(cell){
19124             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19125         });
19126         
19127         days += startingPos;
19128
19129         // convert everything to numbers so it's fast
19130         var day = 86400000;
19131         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19132         //Roo.log(d);
19133         //Roo.log(pm);
19134         //Roo.log(prevStart);
19135         
19136         var today = new Date().clearTime().getTime();
19137         var sel = date.clearTime().getTime();
19138         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19139         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19140         var ddMatch = this.disabledDatesRE;
19141         var ddText = this.disabledDatesText;
19142         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19143         var ddaysText = this.disabledDaysText;
19144         var format = this.format;
19145         
19146         var setCellClass = function(cal, cell){
19147             cell.row = 0;
19148             cell.events = [];
19149             cell.more = [];
19150             //Roo.log('set Cell Class');
19151             cell.title = "";
19152             var t = d.getTime();
19153             
19154             //Roo.log(d);
19155             
19156             cell.dateValue = t;
19157             if(t == today){
19158                 cell.className += " fc-today";
19159                 cell.className += " fc-state-highlight";
19160                 cell.title = cal.todayText;
19161             }
19162             if(t == sel){
19163                 // disable highlight in other month..
19164                 //cell.className += " fc-state-highlight";
19165                 
19166             }
19167             // disabling
19168             if(t < min) {
19169                 cell.className = " fc-state-disabled";
19170                 cell.title = cal.minText;
19171                 return;
19172             }
19173             if(t > max) {
19174                 cell.className = " fc-state-disabled";
19175                 cell.title = cal.maxText;
19176                 return;
19177             }
19178             if(ddays){
19179                 if(ddays.indexOf(d.getDay()) != -1){
19180                     cell.title = ddaysText;
19181                     cell.className = " fc-state-disabled";
19182                 }
19183             }
19184             if(ddMatch && format){
19185                 var fvalue = d.dateFormat(format);
19186                 if(ddMatch.test(fvalue)){
19187                     cell.title = ddText.replace("%0", fvalue);
19188                     cell.className = " fc-state-disabled";
19189                 }
19190             }
19191             
19192             if (!cell.initialClassName) {
19193                 cell.initialClassName = cell.dom.className;
19194             }
19195             
19196             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19197         };
19198
19199         var i = 0;
19200         
19201         for(; i < startingPos; i++) {
19202             textEls[i].innerHTML = (++prevStart);
19203             d.setDate(d.getDate()+1);
19204             
19205             cells[i].className = "fc-past fc-other-month";
19206             setCellClass(this, cells[i]);
19207         }
19208         
19209         var intDay = 0;
19210         
19211         for(; i < days; i++){
19212             intDay = i - startingPos + 1;
19213             textEls[i].innerHTML = (intDay);
19214             d.setDate(d.getDate()+1);
19215             
19216             cells[i].className = ''; // "x-date-active";
19217             setCellClass(this, cells[i]);
19218         }
19219         var extraDays = 0;
19220         
19221         for(; i < 42; i++) {
19222             textEls[i].innerHTML = (++extraDays);
19223             d.setDate(d.getDate()+1);
19224             
19225             cells[i].className = "fc-future fc-other-month";
19226             setCellClass(this, cells[i]);
19227         }
19228         
19229         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19230         
19231         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19232         
19233         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19234         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19235         
19236         if(totalRows != 6){
19237             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19238             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19239         }
19240         
19241         this.fireEvent('monthchange', this, date);
19242         
19243         
19244         /*
19245         if(!this.internalRender){
19246             var main = this.el.dom.firstChild;
19247             var w = main.offsetWidth;
19248             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19249             Roo.fly(main).setWidth(w);
19250             this.internalRender = true;
19251             // opera does not respect the auto grow header center column
19252             // then, after it gets a width opera refuses to recalculate
19253             // without a second pass
19254             if(Roo.isOpera && !this.secondPass){
19255                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19256                 this.secondPass = true;
19257                 this.update.defer(10, this, [date]);
19258             }
19259         }
19260         */
19261         
19262     },
19263     
19264     findCell : function(dt) {
19265         dt = dt.clearTime().getTime();
19266         var ret = false;
19267         this.cells.each(function(c){
19268             //Roo.log("check " +c.dateValue + '?=' + dt);
19269             if(c.dateValue == dt){
19270                 ret = c;
19271                 return false;
19272             }
19273             return true;
19274         });
19275         
19276         return ret;
19277     },
19278     
19279     findCells : function(ev) {
19280         var s = ev.start.clone().clearTime().getTime();
19281        // Roo.log(s);
19282         var e= ev.end.clone().clearTime().getTime();
19283        // Roo.log(e);
19284         var ret = [];
19285         this.cells.each(function(c){
19286              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19287             
19288             if(c.dateValue > e){
19289                 return ;
19290             }
19291             if(c.dateValue < s){
19292                 return ;
19293             }
19294             ret.push(c);
19295         });
19296         
19297         return ret;    
19298     },
19299     
19300 //    findBestRow: function(cells)
19301 //    {
19302 //        var ret = 0;
19303 //        
19304 //        for (var i =0 ; i < cells.length;i++) {
19305 //            ret  = Math.max(cells[i].rows || 0,ret);
19306 //        }
19307 //        return ret;
19308 //        
19309 //    },
19310     
19311     
19312     addItem : function(ev)
19313     {
19314         // look for vertical location slot in
19315         var cells = this.findCells(ev);
19316         
19317 //        ev.row = this.findBestRow(cells);
19318         
19319         // work out the location.
19320         
19321         var crow = false;
19322         var rows = [];
19323         for(var i =0; i < cells.length; i++) {
19324             
19325             cells[i].row = cells[0].row;
19326             
19327             if(i == 0){
19328                 cells[i].row = cells[i].row + 1;
19329             }
19330             
19331             if (!crow) {
19332                 crow = {
19333                     start : cells[i],
19334                     end :  cells[i]
19335                 };
19336                 continue;
19337             }
19338             if (crow.start.getY() == cells[i].getY()) {
19339                 // on same row.
19340                 crow.end = cells[i];
19341                 continue;
19342             }
19343             // different row.
19344             rows.push(crow);
19345             crow = {
19346                 start: cells[i],
19347                 end : cells[i]
19348             };
19349             
19350         }
19351         
19352         rows.push(crow);
19353         ev.els = [];
19354         ev.rows = rows;
19355         ev.cells = cells;
19356         
19357         cells[0].events.push(ev);
19358         
19359         this.calevents.push(ev);
19360     },
19361     
19362     clearEvents: function() {
19363         
19364         if(!this.calevents){
19365             return;
19366         }
19367         
19368         Roo.each(this.cells.elements, function(c){
19369             c.row = 0;
19370             c.events = [];
19371             c.more = [];
19372         });
19373         
19374         Roo.each(this.calevents, function(e) {
19375             Roo.each(e.els, function(el) {
19376                 el.un('mouseenter' ,this.onEventEnter, this);
19377                 el.un('mouseleave' ,this.onEventLeave, this);
19378                 el.remove();
19379             },this);
19380         },this);
19381         
19382         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19383             e.remove();
19384         });
19385         
19386     },
19387     
19388     renderEvents: function()
19389     {   
19390         var _this = this;
19391         
19392         this.cells.each(function(c) {
19393             
19394             if(c.row < 5){
19395                 return;
19396             }
19397             
19398             var ev = c.events;
19399             
19400             var r = 4;
19401             if(c.row != c.events.length){
19402                 r = 4 - (4 - (c.row - c.events.length));
19403             }
19404             
19405             c.events = ev.slice(0, r);
19406             c.more = ev.slice(r);
19407             
19408             if(c.more.length && c.more.length == 1){
19409                 c.events.push(c.more.pop());
19410             }
19411             
19412             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19413             
19414         });
19415             
19416         this.cells.each(function(c) {
19417             
19418             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19419             
19420             
19421             for (var e = 0; e < c.events.length; e++){
19422                 var ev = c.events[e];
19423                 var rows = ev.rows;
19424                 
19425                 for(var i = 0; i < rows.length; i++) {
19426                 
19427                     // how many rows should it span..
19428
19429                     var  cfg = {
19430                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19431                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19432
19433                         unselectable : "on",
19434                         cn : [
19435                             {
19436                                 cls: 'fc-event-inner',
19437                                 cn : [
19438     //                                {
19439     //                                  tag:'span',
19440     //                                  cls: 'fc-event-time',
19441     //                                  html : cells.length > 1 ? '' : ev.time
19442     //                                },
19443                                     {
19444                                       tag:'span',
19445                                       cls: 'fc-event-title',
19446                                       html : String.format('{0}', ev.title)
19447                                     }
19448
19449
19450                                 ]
19451                             },
19452                             {
19453                                 cls: 'ui-resizable-handle ui-resizable-e',
19454                                 html : '&nbsp;&nbsp;&nbsp'
19455                             }
19456
19457                         ]
19458                     };
19459
19460                     if (i == 0) {
19461                         cfg.cls += ' fc-event-start';
19462                     }
19463                     if ((i+1) == rows.length) {
19464                         cfg.cls += ' fc-event-end';
19465                     }
19466
19467                     var ctr = _this.el.select('.fc-event-container',true).first();
19468                     var cg = ctr.createChild(cfg);
19469
19470                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19471                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19472
19473                     var r = (c.more.length) ? 1 : 0;
19474                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19475                     cg.setWidth(ebox.right - sbox.x -2);
19476
19477                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19478                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19479                     cg.on('click', _this.onEventClick, _this, ev);
19480
19481                     ev.els.push(cg);
19482                     
19483                 }
19484                 
19485             }
19486             
19487             
19488             if(c.more.length){
19489                 var  cfg = {
19490                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19491                     style : 'position: absolute',
19492                     unselectable : "on",
19493                     cn : [
19494                         {
19495                             cls: 'fc-event-inner',
19496                             cn : [
19497                                 {
19498                                   tag:'span',
19499                                   cls: 'fc-event-title',
19500                                   html : 'More'
19501                                 }
19502
19503
19504                             ]
19505                         },
19506                         {
19507                             cls: 'ui-resizable-handle ui-resizable-e',
19508                             html : '&nbsp;&nbsp;&nbsp'
19509                         }
19510
19511                     ]
19512                 };
19513
19514                 var ctr = _this.el.select('.fc-event-container',true).first();
19515                 var cg = ctr.createChild(cfg);
19516
19517                 var sbox = c.select('.fc-day-content',true).first().getBox();
19518                 var ebox = c.select('.fc-day-content',true).first().getBox();
19519                 //Roo.log(cg);
19520                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19521                 cg.setWidth(ebox.right - sbox.x -2);
19522
19523                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19524                 
19525             }
19526             
19527         });
19528         
19529         
19530         
19531     },
19532     
19533     onEventEnter: function (e, el,event,d) {
19534         this.fireEvent('evententer', this, el, event);
19535     },
19536     
19537     onEventLeave: function (e, el,event,d) {
19538         this.fireEvent('eventleave', this, el, event);
19539     },
19540     
19541     onEventClick: function (e, el,event,d) {
19542         this.fireEvent('eventclick', this, el, event);
19543     },
19544     
19545     onMonthChange: function () {
19546         this.store.load();
19547     },
19548     
19549     onMoreEventClick: function(e, el, more)
19550     {
19551         var _this = this;
19552         
19553         this.calpopover.placement = 'right';
19554         this.calpopover.setTitle('More');
19555         
19556         this.calpopover.setContent('');
19557         
19558         var ctr = this.calpopover.el.select('.popover-content', true).first();
19559         
19560         Roo.each(more, function(m){
19561             var cfg = {
19562                 cls : 'fc-event-hori fc-event-draggable',
19563                 html : m.title
19564             };
19565             var cg = ctr.createChild(cfg);
19566             
19567             cg.on('click', _this.onEventClick, _this, m);
19568         });
19569         
19570         this.calpopover.show(el);
19571         
19572         
19573     },
19574     
19575     onLoad: function () 
19576     {   
19577         this.calevents = [];
19578         var cal = this;
19579         
19580         if(this.store.getCount() > 0){
19581             this.store.data.each(function(d){
19582                cal.addItem({
19583                     id : d.data.id,
19584                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19585                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19586                     time : d.data.start_time,
19587                     title : d.data.title,
19588                     description : d.data.description,
19589                     venue : d.data.venue
19590                 });
19591             });
19592         }
19593         
19594         this.renderEvents();
19595         
19596         if(this.calevents.length && this.loadMask){
19597             this.maskEl.hide();
19598         }
19599     },
19600     
19601     onBeforeLoad: function()
19602     {
19603         this.clearEvents();
19604         if(this.loadMask){
19605             this.maskEl.show();
19606         }
19607     }
19608 });
19609
19610  
19611  /*
19612  * - LGPL
19613  *
19614  * element
19615  * 
19616  */
19617
19618 /**
19619  * @class Roo.bootstrap.Popover
19620  * @extends Roo.bootstrap.Component
19621  * Bootstrap Popover class
19622  * @cfg {String} html contents of the popover   (or false to use children..)
19623  * @cfg {String} title of popover (or false to hide)
19624  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19625  * @cfg {String} trigger click || hover (or false to trigger manually)
19626  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19627  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19628  *      - if false and it has a 'parent' then it will be automatically added to that element
19629  *      - if string - Roo.get  will be called 
19630  * @cfg {Number} delay - delay before showing
19631  
19632  * @constructor
19633  * Create a new Popover
19634  * @param {Object} config The config object
19635  */
19636
19637 Roo.bootstrap.Popover = function(config){
19638     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19639     
19640     this.addEvents({
19641         // raw events
19642          /**
19643          * @event show
19644          * After the popover show
19645          * 
19646          * @param {Roo.bootstrap.Popover} this
19647          */
19648         "show" : true,
19649         /**
19650          * @event hide
19651          * After the popover hide
19652          * 
19653          * @param {Roo.bootstrap.Popover} this
19654          */
19655         "hide" : true
19656     });
19657 };
19658
19659 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19660     
19661     title: false,
19662     html: false,
19663     
19664     placement : 'right',
19665     trigger : 'hover', // hover
19666     modal : false,
19667     delay : 0,
19668     
19669     over: false,
19670     
19671     can_build_overlaid : false,
19672     
19673     maskEl : false, // the mask element
19674     headerEl : false,
19675     contentEl : false,
19676     alignEl : false, // when show is called with an element - this get's stored.
19677     
19678     getChildContainer : function()
19679     {
19680         return this.contentEl;
19681         
19682     },
19683     getPopoverHeader : function()
19684     {
19685         this.title = true; // flag not to hide it..
19686         this.headerEl.addClass('p-0');
19687         return this.headerEl
19688     },
19689     
19690     
19691     getAutoCreate : function(){
19692          
19693         var cfg = {
19694            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19695            style: 'display:block',
19696            cn : [
19697                 {
19698                     cls : 'arrow'
19699                 },
19700                 {
19701                     cls : 'popover-inner ',
19702                     cn : [
19703                         {
19704                             tag: 'h3',
19705                             cls: 'popover-title popover-header',
19706                             html : this.title === false ? '' : this.title
19707                         },
19708                         {
19709                             cls : 'popover-content popover-body '  + (this.cls || ''),
19710                             html : this.html || ''
19711                         }
19712                     ]
19713                     
19714                 }
19715            ]
19716         };
19717         
19718         return cfg;
19719     },
19720     /**
19721      * @param {string} the title
19722      */
19723     setTitle: function(str)
19724     {
19725         this.title = str;
19726         if (this.el) {
19727             this.headerEl.dom.innerHTML = str;
19728         }
19729         
19730     },
19731     /**
19732      * @param {string} the body content
19733      */
19734     setContent: function(str)
19735     {
19736         this.html = str;
19737         if (this.contentEl) {
19738             this.contentEl.dom.innerHTML = str;
19739         }
19740         
19741     },
19742     // as it get's added to the bottom of the page.
19743     onRender : function(ct, position)
19744     {
19745         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19746         
19747         
19748         
19749         if(!this.el){
19750             var cfg = Roo.apply({},  this.getAutoCreate());
19751             cfg.id = Roo.id();
19752             
19753             if (this.cls) {
19754                 cfg.cls += ' ' + this.cls;
19755             }
19756             if (this.style) {
19757                 cfg.style = this.style;
19758             }
19759             //Roo.log("adding to ");
19760             this.el = Roo.get(document.body).createChild(cfg, position);
19761 //            Roo.log(this.el);
19762         }
19763         
19764         this.contentEl = this.el.select('.popover-content',true).first();
19765         this.headerEl =  this.el.select('.popover-title',true).first();
19766         
19767         var nitems = [];
19768         if(typeof(this.items) != 'undefined'){
19769             var items = this.items;
19770             delete this.items;
19771
19772             for(var i =0;i < items.length;i++) {
19773                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19774             }
19775         }
19776
19777         this.items = nitems;
19778         
19779         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19780         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19781         
19782         
19783         
19784         this.initEvents();
19785     },
19786     
19787     resizeMask : function()
19788     {
19789         this.maskEl.setSize(
19790             Roo.lib.Dom.getViewWidth(true),
19791             Roo.lib.Dom.getViewHeight(true)
19792         );
19793     },
19794     
19795     initEvents : function()
19796     {
19797         
19798         if (!this.modal) { 
19799             Roo.bootstrap.Popover.register(this);
19800         }
19801          
19802         this.arrowEl = this.el.select('.arrow',true).first();
19803         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19804         this.el.enableDisplayMode('block');
19805         this.el.hide();
19806  
19807         
19808         if (this.over === false && !this.parent()) {
19809             return; 
19810         }
19811         if (this.triggers === false) {
19812             return;
19813         }
19814          
19815         // support parent
19816         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19817         var triggers = this.trigger ? this.trigger.split(' ') : [];
19818         Roo.each(triggers, function(trigger) {
19819         
19820             if (trigger == 'click') {
19821                 on_el.on('click', this.toggle, this);
19822             } else if (trigger != 'manual') {
19823                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19824                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19825       
19826                 on_el.on(eventIn  ,this.enter, this);
19827                 on_el.on(eventOut, this.leave, this);
19828             }
19829         }, this);
19830     },
19831     
19832     
19833     // private
19834     timeout : null,
19835     hoverState : null,
19836     
19837     toggle : function () {
19838         this.hoverState == 'in' ? this.leave() : this.enter();
19839     },
19840     
19841     enter : function () {
19842         
19843         clearTimeout(this.timeout);
19844     
19845         this.hoverState = 'in';
19846     
19847         if (!this.delay || !this.delay.show) {
19848             this.show();
19849             return;
19850         }
19851         var _t = this;
19852         this.timeout = setTimeout(function () {
19853             if (_t.hoverState == 'in') {
19854                 _t.show();
19855             }
19856         }, this.delay.show)
19857     },
19858     
19859     leave : function() {
19860         clearTimeout(this.timeout);
19861     
19862         this.hoverState = 'out';
19863     
19864         if (!this.delay || !this.delay.hide) {
19865             this.hide();
19866             return;
19867         }
19868         var _t = this;
19869         this.timeout = setTimeout(function () {
19870             if (_t.hoverState == 'out') {
19871                 _t.hide();
19872             }
19873         }, this.delay.hide)
19874     },
19875     /**
19876      * Show the popover
19877      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19878      * @param {string} (left|right|top|bottom) position
19879      */
19880     show : function (on_el, placement)
19881     {
19882         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19883         on_el = on_el || false; // default to false
19884          
19885         if (!on_el) {
19886             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19887                 on_el = this.parent().el;
19888             } else if (this.over) {
19889                 Roo.get(this.over);
19890             }
19891             
19892         }
19893         
19894         this.alignEl = Roo.get( on_el );
19895
19896         if (!this.el) {
19897             this.render(document.body);
19898         }
19899         
19900         
19901          
19902         
19903         if (this.title === false) {
19904             this.headerEl.hide();
19905         }
19906         
19907        
19908         this.el.show();
19909         this.el.dom.style.display = 'block';
19910          
19911  
19912         if (this.alignEl) {
19913             this.updatePosition(this.placement, true);
19914              
19915         } else {
19916             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19917             var es = this.el.getSize();
19918             var x = Roo.lib.Dom.getViewWidth()/2;
19919             var y = Roo.lib.Dom.getViewHeight()/2;
19920             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19921             
19922         }
19923
19924         
19925         //var arrow = this.el.select('.arrow',true).first();
19926         //arrow.set(align[2], 
19927         
19928         this.el.addClass('in');
19929         
19930          
19931         
19932         this.hoverState = 'in';
19933         
19934         if (this.modal) {
19935             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19936             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19937             this.maskEl.dom.style.display = 'block';
19938             this.maskEl.addClass('show');
19939         }
19940         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19941  
19942         this.fireEvent('show', this);
19943         
19944     },
19945     /**
19946      * fire this manually after loading a grid in the table for example
19947      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19948      * @param {Boolean} try and move it if we cant get right position.
19949      */
19950     updatePosition : function(placement, try_move)
19951     {
19952         // allow for calling with no parameters
19953         placement = placement   ? placement :  this.placement;
19954         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19955         
19956         this.el.removeClass([
19957             'fade','top','bottom', 'left', 'right','in',
19958             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19959         ]);
19960         this.el.addClass(placement + ' bs-popover-' + placement);
19961         
19962         if (!this.alignEl ) {
19963             return false;
19964         }
19965         
19966         switch (placement) {
19967             case 'right':
19968                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19969                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19970                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19971                     //normal display... or moved up/down.
19972                     this.el.setXY(offset);
19973                     var xy = this.alignEl.getAnchorXY('tr', false);
19974                     xy[0]+=2;xy[1]+=5;
19975                     this.arrowEl.setXY(xy);
19976                     return true;
19977                 }
19978                 // continue through...
19979                 return this.updatePosition('left', false);
19980                 
19981             
19982             case 'left':
19983                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19984                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19985                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19986                     //normal display... or moved up/down.
19987                     this.el.setXY(offset);
19988                     var xy = this.alignEl.getAnchorXY('tl', false);
19989                     xy[0]-=10;xy[1]+=5; // << fix me
19990                     this.arrowEl.setXY(xy);
19991                     return true;
19992                 }
19993                 // call self...
19994                 return this.updatePosition('right', false);
19995             
19996             case 'top':
19997                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
19998                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
19999                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20000                     //normal display... or moved up/down.
20001                     this.el.setXY(offset);
20002                     var xy = this.alignEl.getAnchorXY('t', false);
20003                     xy[1]-=10; // << fix me
20004                     this.arrowEl.setXY(xy);
20005                     return true;
20006                 }
20007                 // fall through
20008                return this.updatePosition('bottom', false);
20009             
20010             case 'bottom':
20011                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20012                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20013                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20014                     //normal display... or moved up/down.
20015                     this.el.setXY(offset);
20016                     var xy = this.alignEl.getAnchorXY('b', false);
20017                      xy[1]+=2; // << fix me
20018                     this.arrowEl.setXY(xy);
20019                     return true;
20020                 }
20021                 // fall through
20022                 return this.updatePosition('top', false);
20023                 
20024             
20025         }
20026         
20027         
20028         return false;
20029     },
20030     
20031     hide : function()
20032     {
20033         this.el.setXY([0,0]);
20034         this.el.removeClass('in');
20035         this.el.hide();
20036         this.hoverState = null;
20037         this.maskEl.hide(); // always..
20038         this.fireEvent('hide', this);
20039     }
20040     
20041 });
20042
20043
20044 Roo.apply(Roo.bootstrap.Popover, {
20045
20046     alignment : {
20047         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20048         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20049         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20050         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20051     },
20052     
20053     zIndex : 20001,
20054
20055     clickHander : false,
20056     
20057
20058     onMouseDown : function(e)
20059     {
20060         if (!e.getTarget(".roo-popover")) {
20061             this.hideAll();
20062         }
20063          
20064     },
20065     
20066     popups : [],
20067     
20068     register : function(popup)
20069     {
20070         if (!Roo.bootstrap.Popover.clickHandler) {
20071             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20072         }
20073         // hide other popups.
20074         this.hideAll();
20075         this.popups.push(popup);
20076     },
20077     hideAll : function()
20078     {
20079         this.popups.forEach(function(p) {
20080             p.hide();
20081         });
20082     }
20083
20084 });/*
20085  * - LGPL
20086  *
20087  * Card header - holder for the card header elements.
20088  * 
20089  */
20090
20091 /**
20092  * @class Roo.bootstrap.PopoverNav
20093  * @extends Roo.bootstrap.NavGroup
20094  * Bootstrap Popover header navigation class
20095  * @constructor
20096  * Create a new Popover Header Navigation 
20097  * @param {Object} config The config object
20098  */
20099
20100 Roo.bootstrap.PopoverNav = function(config){
20101     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20102 };
20103
20104 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20105     
20106     
20107     container_method : 'getPopoverHeader' 
20108     
20109      
20110     
20111     
20112    
20113 });
20114
20115  
20116
20117  /*
20118  * - LGPL
20119  *
20120  * Progress
20121  * 
20122  */
20123
20124 /**
20125  * @class Roo.bootstrap.Progress
20126  * @extends Roo.bootstrap.Component
20127  * Bootstrap Progress class
20128  * @cfg {Boolean} striped striped of the progress bar
20129  * @cfg {Boolean} active animated of the progress bar
20130  * 
20131  * 
20132  * @constructor
20133  * Create a new Progress
20134  * @param {Object} config The config object
20135  */
20136
20137 Roo.bootstrap.Progress = function(config){
20138     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20139 };
20140
20141 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20142     
20143     striped : false,
20144     active: false,
20145     
20146     getAutoCreate : function(){
20147         var cfg = {
20148             tag: 'div',
20149             cls: 'progress'
20150         };
20151         
20152         
20153         if(this.striped){
20154             cfg.cls += ' progress-striped';
20155         }
20156       
20157         if(this.active){
20158             cfg.cls += ' active';
20159         }
20160         
20161         
20162         return cfg;
20163     }
20164    
20165 });
20166
20167  
20168
20169  /*
20170  * - LGPL
20171  *
20172  * ProgressBar
20173  * 
20174  */
20175
20176 /**
20177  * @class Roo.bootstrap.ProgressBar
20178  * @extends Roo.bootstrap.Component
20179  * Bootstrap ProgressBar class
20180  * @cfg {Number} aria_valuenow aria-value now
20181  * @cfg {Number} aria_valuemin aria-value min
20182  * @cfg {Number} aria_valuemax aria-value max
20183  * @cfg {String} label label for the progress bar
20184  * @cfg {String} panel (success | info | warning | danger )
20185  * @cfg {String} role role of the progress bar
20186  * @cfg {String} sr_only text
20187  * 
20188  * 
20189  * @constructor
20190  * Create a new ProgressBar
20191  * @param {Object} config The config object
20192  */
20193
20194 Roo.bootstrap.ProgressBar = function(config){
20195     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20196 };
20197
20198 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20199     
20200     aria_valuenow : 0,
20201     aria_valuemin : 0,
20202     aria_valuemax : 100,
20203     label : false,
20204     panel : false,
20205     role : false,
20206     sr_only: false,
20207     
20208     getAutoCreate : function()
20209     {
20210         
20211         var cfg = {
20212             tag: 'div',
20213             cls: 'progress-bar',
20214             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20215         };
20216         
20217         if(this.sr_only){
20218             cfg.cn = {
20219                 tag: 'span',
20220                 cls: 'sr-only',
20221                 html: this.sr_only
20222             }
20223         }
20224         
20225         if(this.role){
20226             cfg.role = this.role;
20227         }
20228         
20229         if(this.aria_valuenow){
20230             cfg['aria-valuenow'] = this.aria_valuenow;
20231         }
20232         
20233         if(this.aria_valuemin){
20234             cfg['aria-valuemin'] = this.aria_valuemin;
20235         }
20236         
20237         if(this.aria_valuemax){
20238             cfg['aria-valuemax'] = this.aria_valuemax;
20239         }
20240         
20241         if(this.label && !this.sr_only){
20242             cfg.html = this.label;
20243         }
20244         
20245         if(this.panel){
20246             cfg.cls += ' progress-bar-' + this.panel;
20247         }
20248         
20249         return cfg;
20250     },
20251     
20252     update : function(aria_valuenow)
20253     {
20254         this.aria_valuenow = aria_valuenow;
20255         
20256         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20257     }
20258    
20259 });
20260
20261  
20262
20263  /*
20264  * - LGPL
20265  *
20266  * column
20267  * 
20268  */
20269
20270 /**
20271  * @class Roo.bootstrap.TabGroup
20272  * @extends Roo.bootstrap.Column
20273  * Bootstrap Column class
20274  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20275  * @cfg {Boolean} carousel true to make the group behave like a carousel
20276  * @cfg {Boolean} bullets show bullets for the panels
20277  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20278  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20279  * @cfg {Boolean} showarrow (true|false) show arrow default true
20280  * 
20281  * @constructor
20282  * Create a new TabGroup
20283  * @param {Object} config The config object
20284  */
20285
20286 Roo.bootstrap.TabGroup = function(config){
20287     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20288     if (!this.navId) {
20289         this.navId = Roo.id();
20290     }
20291     this.tabs = [];
20292     Roo.bootstrap.TabGroup.register(this);
20293     
20294 };
20295
20296 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20297     
20298     carousel : false,
20299     transition : false,
20300     bullets : 0,
20301     timer : 0,
20302     autoslide : false,
20303     slideFn : false,
20304     slideOnTouch : false,
20305     showarrow : true,
20306     
20307     getAutoCreate : function()
20308     {
20309         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20310         
20311         cfg.cls += ' tab-content';
20312         
20313         if (this.carousel) {
20314             cfg.cls += ' carousel slide';
20315             
20316             cfg.cn = [{
20317                cls : 'carousel-inner',
20318                cn : []
20319             }];
20320         
20321             if(this.bullets  && !Roo.isTouch){
20322                 
20323                 var bullets = {
20324                     cls : 'carousel-bullets',
20325                     cn : []
20326                 };
20327                
20328                 if(this.bullets_cls){
20329                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20330                 }
20331                 
20332                 bullets.cn.push({
20333                     cls : 'clear'
20334                 });
20335                 
20336                 cfg.cn[0].cn.push(bullets);
20337             }
20338             
20339             if(this.showarrow){
20340                 cfg.cn[0].cn.push({
20341                     tag : 'div',
20342                     class : 'carousel-arrow',
20343                     cn : [
20344                         {
20345                             tag : 'div',
20346                             class : 'carousel-prev',
20347                             cn : [
20348                                 {
20349                                     tag : 'i',
20350                                     class : 'fa fa-chevron-left'
20351                                 }
20352                             ]
20353                         },
20354                         {
20355                             tag : 'div',
20356                             class : 'carousel-next',
20357                             cn : [
20358                                 {
20359                                     tag : 'i',
20360                                     class : 'fa fa-chevron-right'
20361                                 }
20362                             ]
20363                         }
20364                     ]
20365                 });
20366             }
20367             
20368         }
20369         
20370         return cfg;
20371     },
20372     
20373     initEvents:  function()
20374     {
20375 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20376 //            this.el.on("touchstart", this.onTouchStart, this);
20377 //        }
20378         
20379         if(this.autoslide){
20380             var _this = this;
20381             
20382             this.slideFn = window.setInterval(function() {
20383                 _this.showPanelNext();
20384             }, this.timer);
20385         }
20386         
20387         if(this.showarrow){
20388             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20389             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20390         }
20391         
20392         
20393     },
20394     
20395 //    onTouchStart : function(e, el, o)
20396 //    {
20397 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20398 //            return;
20399 //        }
20400 //        
20401 //        this.showPanelNext();
20402 //    },
20403     
20404     
20405     getChildContainer : function()
20406     {
20407         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20408     },
20409     
20410     /**
20411     * register a Navigation item
20412     * @param {Roo.bootstrap.NavItem} the navitem to add
20413     */
20414     register : function(item)
20415     {
20416         this.tabs.push( item);
20417         item.navId = this.navId; // not really needed..
20418         this.addBullet();
20419     
20420     },
20421     
20422     getActivePanel : function()
20423     {
20424         var r = false;
20425         Roo.each(this.tabs, function(t) {
20426             if (t.active) {
20427                 r = t;
20428                 return false;
20429             }
20430             return null;
20431         });
20432         return r;
20433         
20434     },
20435     getPanelByName : function(n)
20436     {
20437         var r = false;
20438         Roo.each(this.tabs, function(t) {
20439             if (t.tabId == n) {
20440                 r = t;
20441                 return false;
20442             }
20443             return null;
20444         });
20445         return r;
20446     },
20447     indexOfPanel : function(p)
20448     {
20449         var r = false;
20450         Roo.each(this.tabs, function(t,i) {
20451             if (t.tabId == p.tabId) {
20452                 r = i;
20453                 return false;
20454             }
20455             return null;
20456         });
20457         return r;
20458     },
20459     /**
20460      * show a specific panel
20461      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20462      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20463      */
20464     showPanel : function (pan)
20465     {
20466         if(this.transition || typeof(pan) == 'undefined'){
20467             Roo.log("waiting for the transitionend");
20468             return false;
20469         }
20470         
20471         if (typeof(pan) == 'number') {
20472             pan = this.tabs[pan];
20473         }
20474         
20475         if (typeof(pan) == 'string') {
20476             pan = this.getPanelByName(pan);
20477         }
20478         
20479         var cur = this.getActivePanel();
20480         
20481         if(!pan || !cur){
20482             Roo.log('pan or acitve pan is undefined');
20483             return false;
20484         }
20485         
20486         if (pan.tabId == this.getActivePanel().tabId) {
20487             return true;
20488         }
20489         
20490         if (false === cur.fireEvent('beforedeactivate')) {
20491             return false;
20492         }
20493         
20494         if(this.bullets > 0 && !Roo.isTouch){
20495             this.setActiveBullet(this.indexOfPanel(pan));
20496         }
20497         
20498         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20499             
20500             //class="carousel-item carousel-item-next carousel-item-left"
20501             
20502             this.transition = true;
20503             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20504             var lr = dir == 'next' ? 'left' : 'right';
20505             pan.el.addClass(dir); // or prev
20506             pan.el.addClass('carousel-item-' + dir); // or prev
20507             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20508             cur.el.addClass(lr); // or right
20509             pan.el.addClass(lr);
20510             cur.el.addClass('carousel-item-' +lr); // or right
20511             pan.el.addClass('carousel-item-' +lr);
20512             
20513             
20514             var _this = this;
20515             cur.el.on('transitionend', function() {
20516                 Roo.log("trans end?");
20517                 
20518                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20519                 pan.setActive(true);
20520                 
20521                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20522                 cur.setActive(false);
20523                 
20524                 _this.transition = false;
20525                 
20526             }, this, { single:  true } );
20527             
20528             return true;
20529         }
20530         
20531         cur.setActive(false);
20532         pan.setActive(true);
20533         
20534         return true;
20535         
20536     },
20537     showPanelNext : function()
20538     {
20539         var i = this.indexOfPanel(this.getActivePanel());
20540         
20541         if (i >= this.tabs.length - 1 && !this.autoslide) {
20542             return;
20543         }
20544         
20545         if (i >= this.tabs.length - 1 && this.autoslide) {
20546             i = -1;
20547         }
20548         
20549         this.showPanel(this.tabs[i+1]);
20550     },
20551     
20552     showPanelPrev : function()
20553     {
20554         var i = this.indexOfPanel(this.getActivePanel());
20555         
20556         if (i  < 1 && !this.autoslide) {
20557             return;
20558         }
20559         
20560         if (i < 1 && this.autoslide) {
20561             i = this.tabs.length;
20562         }
20563         
20564         this.showPanel(this.tabs[i-1]);
20565     },
20566     
20567     
20568     addBullet: function()
20569     {
20570         if(!this.bullets || Roo.isTouch){
20571             return;
20572         }
20573         var ctr = this.el.select('.carousel-bullets',true).first();
20574         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20575         var bullet = ctr.createChild({
20576             cls : 'bullet bullet-' + i
20577         },ctr.dom.lastChild);
20578         
20579         
20580         var _this = this;
20581         
20582         bullet.on('click', (function(e, el, o, ii, t){
20583
20584             e.preventDefault();
20585
20586             this.showPanel(ii);
20587
20588             if(this.autoslide && this.slideFn){
20589                 clearInterval(this.slideFn);
20590                 this.slideFn = window.setInterval(function() {
20591                     _this.showPanelNext();
20592                 }, this.timer);
20593             }
20594
20595         }).createDelegate(this, [i, bullet], true));
20596                 
20597         
20598     },
20599      
20600     setActiveBullet : function(i)
20601     {
20602         if(Roo.isTouch){
20603             return;
20604         }
20605         
20606         Roo.each(this.el.select('.bullet', true).elements, function(el){
20607             el.removeClass('selected');
20608         });
20609
20610         var bullet = this.el.select('.bullet-' + i, true).first();
20611         
20612         if(!bullet){
20613             return;
20614         }
20615         
20616         bullet.addClass('selected');
20617     }
20618     
20619     
20620   
20621 });
20622
20623  
20624
20625  
20626  
20627 Roo.apply(Roo.bootstrap.TabGroup, {
20628     
20629     groups: {},
20630      /**
20631     * register a Navigation Group
20632     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20633     */
20634     register : function(navgrp)
20635     {
20636         this.groups[navgrp.navId] = navgrp;
20637         
20638     },
20639     /**
20640     * fetch a Navigation Group based on the navigation ID
20641     * if one does not exist , it will get created.
20642     * @param {string} the navgroup to add
20643     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20644     */
20645     get: function(navId) {
20646         if (typeof(this.groups[navId]) == 'undefined') {
20647             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20648         }
20649         return this.groups[navId] ;
20650     }
20651     
20652     
20653     
20654 });
20655
20656  /*
20657  * - LGPL
20658  *
20659  * TabPanel
20660  * 
20661  */
20662
20663 /**
20664  * @class Roo.bootstrap.TabPanel
20665  * @extends Roo.bootstrap.Component
20666  * Bootstrap TabPanel class
20667  * @cfg {Boolean} active panel active
20668  * @cfg {String} html panel content
20669  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20670  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20671  * @cfg {String} href click to link..
20672  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20673  * 
20674  * 
20675  * @constructor
20676  * Create a new TabPanel
20677  * @param {Object} config The config object
20678  */
20679
20680 Roo.bootstrap.TabPanel = function(config){
20681     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20682     this.addEvents({
20683         /**
20684              * @event changed
20685              * Fires when the active status changes
20686              * @param {Roo.bootstrap.TabPanel} this
20687              * @param {Boolean} state the new state
20688             
20689          */
20690         'changed': true,
20691         /**
20692              * @event beforedeactivate
20693              * Fires before a tab is de-activated - can be used to do validation on a form.
20694              * @param {Roo.bootstrap.TabPanel} this
20695              * @return {Boolean} false if there is an error
20696             
20697          */
20698         'beforedeactivate': true
20699      });
20700     
20701     this.tabId = this.tabId || Roo.id();
20702   
20703 };
20704
20705 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20706     
20707     active: false,
20708     html: false,
20709     tabId: false,
20710     navId : false,
20711     href : '',
20712     touchSlide : false,
20713     getAutoCreate : function(){
20714         
20715         
20716         var cfg = {
20717             tag: 'div',
20718             // item is needed for carousel - not sure if it has any effect otherwise
20719             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20720             html: this.html || ''
20721         };
20722         
20723         if(this.active){
20724             cfg.cls += ' active';
20725         }
20726         
20727         if(this.tabId){
20728             cfg.tabId = this.tabId;
20729         }
20730         
20731         
20732         
20733         return cfg;
20734     },
20735     
20736     initEvents:  function()
20737     {
20738         var p = this.parent();
20739         
20740         this.navId = this.navId || p.navId;
20741         
20742         if (typeof(this.navId) != 'undefined') {
20743             // not really needed.. but just in case.. parent should be a NavGroup.
20744             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20745             
20746             tg.register(this);
20747             
20748             var i = tg.tabs.length - 1;
20749             
20750             if(this.active && tg.bullets > 0 && i < tg.bullets){
20751                 tg.setActiveBullet(i);
20752             }
20753         }
20754         
20755         this.el.on('click', this.onClick, this);
20756         
20757         if(Roo.isTouch && this.touchSlide){
20758             this.el.on("touchstart", this.onTouchStart, this);
20759             this.el.on("touchmove", this.onTouchMove, this);
20760             this.el.on("touchend", this.onTouchEnd, this);
20761         }
20762         
20763     },
20764     
20765     onRender : function(ct, position)
20766     {
20767         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20768     },
20769     
20770     setActive : function(state)
20771     {
20772         Roo.log("panel - set active " + this.tabId + "=" + state);
20773         
20774         this.active = state;
20775         if (!state) {
20776             this.el.removeClass('active');
20777             
20778         } else  if (!this.el.hasClass('active')) {
20779             this.el.addClass('active');
20780         }
20781         
20782         this.fireEvent('changed', this, state);
20783     },
20784     
20785     onClick : function(e)
20786     {
20787         e.preventDefault();
20788         
20789         if(!this.href.length){
20790             return;
20791         }
20792         
20793         window.location.href = this.href;
20794     },
20795     
20796     startX : 0,
20797     startY : 0,
20798     endX : 0,
20799     endY : 0,
20800     swiping : false,
20801     
20802     onTouchStart : function(e)
20803     {
20804         this.swiping = false;
20805         
20806         this.startX = e.browserEvent.touches[0].clientX;
20807         this.startY = e.browserEvent.touches[0].clientY;
20808     },
20809     
20810     onTouchMove : function(e)
20811     {
20812         this.swiping = true;
20813         
20814         this.endX = e.browserEvent.touches[0].clientX;
20815         this.endY = e.browserEvent.touches[0].clientY;
20816     },
20817     
20818     onTouchEnd : function(e)
20819     {
20820         if(!this.swiping){
20821             this.onClick(e);
20822             return;
20823         }
20824         
20825         var tabGroup = this.parent();
20826         
20827         if(this.endX > this.startX){ // swiping right
20828             tabGroup.showPanelPrev();
20829             return;
20830         }
20831         
20832         if(this.startX > this.endX){ // swiping left
20833             tabGroup.showPanelNext();
20834             return;
20835         }
20836     }
20837     
20838     
20839 });
20840  
20841
20842  
20843
20844  /*
20845  * - LGPL
20846  *
20847  * DateField
20848  * 
20849  */
20850
20851 /**
20852  * @class Roo.bootstrap.DateField
20853  * @extends Roo.bootstrap.Input
20854  * Bootstrap DateField class
20855  * @cfg {Number} weekStart default 0
20856  * @cfg {String} viewMode default empty, (months|years)
20857  * @cfg {String} minViewMode default empty, (months|years)
20858  * @cfg {Number} startDate default -Infinity
20859  * @cfg {Number} endDate default Infinity
20860  * @cfg {Boolean} todayHighlight default false
20861  * @cfg {Boolean} todayBtn default false
20862  * @cfg {Boolean} calendarWeeks default false
20863  * @cfg {Object} daysOfWeekDisabled default empty
20864  * @cfg {Boolean} singleMode default false (true | false)
20865  * 
20866  * @cfg {Boolean} keyboardNavigation default true
20867  * @cfg {String} language default en
20868  * 
20869  * @constructor
20870  * Create a new DateField
20871  * @param {Object} config The config object
20872  */
20873
20874 Roo.bootstrap.DateField = function(config){
20875     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20876      this.addEvents({
20877             /**
20878              * @event show
20879              * Fires when this field show.
20880              * @param {Roo.bootstrap.DateField} this
20881              * @param {Mixed} date The date value
20882              */
20883             show : true,
20884             /**
20885              * @event show
20886              * Fires when this field hide.
20887              * @param {Roo.bootstrap.DateField} this
20888              * @param {Mixed} date The date value
20889              */
20890             hide : true,
20891             /**
20892              * @event select
20893              * Fires when select a date.
20894              * @param {Roo.bootstrap.DateField} this
20895              * @param {Mixed} date The date value
20896              */
20897             select : true,
20898             /**
20899              * @event beforeselect
20900              * Fires when before select a date.
20901              * @param {Roo.bootstrap.DateField} this
20902              * @param {Mixed} date The date value
20903              */
20904             beforeselect : true
20905         });
20906 };
20907
20908 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20909     
20910     /**
20911      * @cfg {String} format
20912      * The default date format string which can be overriden for localization support.  The format must be
20913      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20914      */
20915     format : "m/d/y",
20916     /**
20917      * @cfg {String} altFormats
20918      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20919      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20920      */
20921     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20922     
20923     weekStart : 0,
20924     
20925     viewMode : '',
20926     
20927     minViewMode : '',
20928     
20929     todayHighlight : false,
20930     
20931     todayBtn: false,
20932     
20933     language: 'en',
20934     
20935     keyboardNavigation: true,
20936     
20937     calendarWeeks: false,
20938     
20939     startDate: -Infinity,
20940     
20941     endDate: Infinity,
20942     
20943     daysOfWeekDisabled: [],
20944     
20945     _events: [],
20946     
20947     singleMode : false,
20948     
20949     UTCDate: function()
20950     {
20951         return new Date(Date.UTC.apply(Date, arguments));
20952     },
20953     
20954     UTCToday: function()
20955     {
20956         var today = new Date();
20957         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20958     },
20959     
20960     getDate: function() {
20961             var d = this.getUTCDate();
20962             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20963     },
20964     
20965     getUTCDate: function() {
20966             return this.date;
20967     },
20968     
20969     setDate: function(d) {
20970             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20971     },
20972     
20973     setUTCDate: function(d) {
20974             this.date = d;
20975             this.setValue(this.formatDate(this.date));
20976     },
20977         
20978     onRender: function(ct, position)
20979     {
20980         
20981         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20982         
20983         this.language = this.language || 'en';
20984         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20985         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20986         
20987         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20988         this.format = this.format || 'm/d/y';
20989         this.isInline = false;
20990         this.isInput = true;
20991         this.component = this.el.select('.add-on', true).first() || false;
20992         this.component = (this.component && this.component.length === 0) ? false : this.component;
20993         this.hasInput = this.component && this.inputEl().length;
20994         
20995         if (typeof(this.minViewMode === 'string')) {
20996             switch (this.minViewMode) {
20997                 case 'months':
20998                     this.minViewMode = 1;
20999                     break;
21000                 case 'years':
21001                     this.minViewMode = 2;
21002                     break;
21003                 default:
21004                     this.minViewMode = 0;
21005                     break;
21006             }
21007         }
21008         
21009         if (typeof(this.viewMode === 'string')) {
21010             switch (this.viewMode) {
21011                 case 'months':
21012                     this.viewMode = 1;
21013                     break;
21014                 case 'years':
21015                     this.viewMode = 2;
21016                     break;
21017                 default:
21018                     this.viewMode = 0;
21019                     break;
21020             }
21021         }
21022                 
21023         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21024         
21025 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21026         
21027         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21028         
21029         this.picker().on('mousedown', this.onMousedown, this);
21030         this.picker().on('click', this.onClick, this);
21031         
21032         this.picker().addClass('datepicker-dropdown');
21033         
21034         this.startViewMode = this.viewMode;
21035         
21036         if(this.singleMode){
21037             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21038                 v.setVisibilityMode(Roo.Element.DISPLAY);
21039                 v.hide();
21040             });
21041             
21042             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21043                 v.setStyle('width', '189px');
21044             });
21045         }
21046         
21047         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21048             if(!this.calendarWeeks){
21049                 v.remove();
21050                 return;
21051             }
21052             
21053             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21054             v.attr('colspan', function(i, val){
21055                 return parseInt(val) + 1;
21056             });
21057         });
21058                         
21059         
21060         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21061         
21062         this.setStartDate(this.startDate);
21063         this.setEndDate(this.endDate);
21064         
21065         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21066         
21067         this.fillDow();
21068         this.fillMonths();
21069         this.update();
21070         this.showMode();
21071         
21072         if(this.isInline) {
21073             this.showPopup();
21074         }
21075     },
21076     
21077     picker : function()
21078     {
21079         return this.pickerEl;
21080 //        return this.el.select('.datepicker', true).first();
21081     },
21082     
21083     fillDow: function()
21084     {
21085         var dowCnt = this.weekStart;
21086         
21087         var dow = {
21088             tag: 'tr',
21089             cn: [
21090                 
21091             ]
21092         };
21093         
21094         if(this.calendarWeeks){
21095             dow.cn.push({
21096                 tag: 'th',
21097                 cls: 'cw',
21098                 html: '&nbsp;'
21099             })
21100         }
21101         
21102         while (dowCnt < this.weekStart + 7) {
21103             dow.cn.push({
21104                 tag: 'th',
21105                 cls: 'dow',
21106                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21107             });
21108         }
21109         
21110         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21111     },
21112     
21113     fillMonths: function()
21114     {    
21115         var i = 0;
21116         var months = this.picker().select('>.datepicker-months td', true).first();
21117         
21118         months.dom.innerHTML = '';
21119         
21120         while (i < 12) {
21121             var month = {
21122                 tag: 'span',
21123                 cls: 'month',
21124                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21125             };
21126             
21127             months.createChild(month);
21128         }
21129         
21130     },
21131     
21132     update: function()
21133     {
21134         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;
21135         
21136         if (this.date < this.startDate) {
21137             this.viewDate = new Date(this.startDate);
21138         } else if (this.date > this.endDate) {
21139             this.viewDate = new Date(this.endDate);
21140         } else {
21141             this.viewDate = new Date(this.date);
21142         }
21143         
21144         this.fill();
21145     },
21146     
21147     fill: function() 
21148     {
21149         var d = new Date(this.viewDate),
21150                 year = d.getUTCFullYear(),
21151                 month = d.getUTCMonth(),
21152                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21153                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21154                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21155                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21156                 currentDate = this.date && this.date.valueOf(),
21157                 today = this.UTCToday();
21158         
21159         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21160         
21161 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21162         
21163 //        this.picker.select('>tfoot th.today').
21164 //                                              .text(dates[this.language].today)
21165 //                                              .toggle(this.todayBtn !== false);
21166     
21167         this.updateNavArrows();
21168         this.fillMonths();
21169                                                 
21170         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21171         
21172         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21173          
21174         prevMonth.setUTCDate(day);
21175         
21176         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21177         
21178         var nextMonth = new Date(prevMonth);
21179         
21180         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21181         
21182         nextMonth = nextMonth.valueOf();
21183         
21184         var fillMonths = false;
21185         
21186         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21187         
21188         while(prevMonth.valueOf() <= nextMonth) {
21189             var clsName = '';
21190             
21191             if (prevMonth.getUTCDay() === this.weekStart) {
21192                 if(fillMonths){
21193                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21194                 }
21195                     
21196                 fillMonths = {
21197                     tag: 'tr',
21198                     cn: []
21199                 };
21200                 
21201                 if(this.calendarWeeks){
21202                     // ISO 8601: First week contains first thursday.
21203                     // ISO also states week starts on Monday, but we can be more abstract here.
21204                     var
21205                     // Start of current week: based on weekstart/current date
21206                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21207                     // Thursday of this week
21208                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21209                     // First Thursday of year, year from thursday
21210                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21211                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21212                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21213                     
21214                     fillMonths.cn.push({
21215                         tag: 'td',
21216                         cls: 'cw',
21217                         html: calWeek
21218                     });
21219                 }
21220             }
21221             
21222             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21223                 clsName += ' old';
21224             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21225                 clsName += ' new';
21226             }
21227             if (this.todayHighlight &&
21228                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21229                 prevMonth.getUTCMonth() == today.getMonth() &&
21230                 prevMonth.getUTCDate() == today.getDate()) {
21231                 clsName += ' today';
21232             }
21233             
21234             if (currentDate && prevMonth.valueOf() === currentDate) {
21235                 clsName += ' active';
21236             }
21237             
21238             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21239                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21240                     clsName += ' disabled';
21241             }
21242             
21243             fillMonths.cn.push({
21244                 tag: 'td',
21245                 cls: 'day ' + clsName,
21246                 html: prevMonth.getDate()
21247             });
21248             
21249             prevMonth.setDate(prevMonth.getDate()+1);
21250         }
21251           
21252         var currentYear = this.date && this.date.getUTCFullYear();
21253         var currentMonth = this.date && this.date.getUTCMonth();
21254         
21255         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21256         
21257         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21258             v.removeClass('active');
21259             
21260             if(currentYear === year && k === currentMonth){
21261                 v.addClass('active');
21262             }
21263             
21264             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21265                 v.addClass('disabled');
21266             }
21267             
21268         });
21269         
21270         
21271         year = parseInt(year/10, 10) * 10;
21272         
21273         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21274         
21275         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21276         
21277         year -= 1;
21278         for (var i = -1; i < 11; i++) {
21279             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21280                 tag: 'span',
21281                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21282                 html: year
21283             });
21284             
21285             year += 1;
21286         }
21287     },
21288     
21289     showMode: function(dir) 
21290     {
21291         if (dir) {
21292             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21293         }
21294         
21295         Roo.each(this.picker().select('>div',true).elements, function(v){
21296             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21297             v.hide();
21298         });
21299         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21300     },
21301     
21302     place: function()
21303     {
21304         if(this.isInline) {
21305             return;
21306         }
21307         
21308         this.picker().removeClass(['bottom', 'top']);
21309         
21310         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21311             /*
21312              * place to the top of element!
21313              *
21314              */
21315             
21316             this.picker().addClass('top');
21317             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21318             
21319             return;
21320         }
21321         
21322         this.picker().addClass('bottom');
21323         
21324         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21325     },
21326     
21327     parseDate : function(value)
21328     {
21329         if(!value || value instanceof Date){
21330             return value;
21331         }
21332         var v = Date.parseDate(value, this.format);
21333         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21334             v = Date.parseDate(value, 'Y-m-d');
21335         }
21336         if(!v && this.altFormats){
21337             if(!this.altFormatsArray){
21338                 this.altFormatsArray = this.altFormats.split("|");
21339             }
21340             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21341                 v = Date.parseDate(value, this.altFormatsArray[i]);
21342             }
21343         }
21344         return v;
21345     },
21346     
21347     formatDate : function(date, fmt)
21348     {   
21349         return (!date || !(date instanceof Date)) ?
21350         date : date.dateFormat(fmt || this.format);
21351     },
21352     
21353     onFocus : function()
21354     {
21355         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21356         this.showPopup();
21357     },
21358     
21359     onBlur : function()
21360     {
21361         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21362         
21363         var d = this.inputEl().getValue();
21364         
21365         this.setValue(d);
21366                 
21367         this.hidePopup();
21368     },
21369     
21370     showPopup : function()
21371     {
21372         this.picker().show();
21373         this.update();
21374         this.place();
21375         
21376         this.fireEvent('showpopup', this, this.date);
21377     },
21378     
21379     hidePopup : function()
21380     {
21381         if(this.isInline) {
21382             return;
21383         }
21384         this.picker().hide();
21385         this.viewMode = this.startViewMode;
21386         this.showMode();
21387         
21388         this.fireEvent('hidepopup', this, this.date);
21389         
21390     },
21391     
21392     onMousedown: function(e)
21393     {
21394         e.stopPropagation();
21395         e.preventDefault();
21396     },
21397     
21398     keyup: function(e)
21399     {
21400         Roo.bootstrap.DateField.superclass.keyup.call(this);
21401         this.update();
21402     },
21403
21404     setValue: function(v)
21405     {
21406         if(this.fireEvent('beforeselect', this, v) !== false){
21407             var d = new Date(this.parseDate(v) ).clearTime();
21408         
21409             if(isNaN(d.getTime())){
21410                 this.date = this.viewDate = '';
21411                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21412                 return;
21413             }
21414
21415             v = this.formatDate(d);
21416
21417             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21418
21419             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21420
21421             this.update();
21422
21423             this.fireEvent('select', this, this.date);
21424         }
21425     },
21426     
21427     getValue: function()
21428     {
21429         return this.formatDate(this.date);
21430     },
21431     
21432     fireKey: function(e)
21433     {
21434         if (!this.picker().isVisible()){
21435             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21436                 this.showPopup();
21437             }
21438             return;
21439         }
21440         
21441         var dateChanged = false,
21442         dir, day, month,
21443         newDate, newViewDate;
21444         
21445         switch(e.keyCode){
21446             case 27: // escape
21447                 this.hidePopup();
21448                 e.preventDefault();
21449                 break;
21450             case 37: // left
21451             case 39: // right
21452                 if (!this.keyboardNavigation) {
21453                     break;
21454                 }
21455                 dir = e.keyCode == 37 ? -1 : 1;
21456                 
21457                 if (e.ctrlKey){
21458                     newDate = this.moveYear(this.date, dir);
21459                     newViewDate = this.moveYear(this.viewDate, dir);
21460                 } else if (e.shiftKey){
21461                     newDate = this.moveMonth(this.date, dir);
21462                     newViewDate = this.moveMonth(this.viewDate, dir);
21463                 } else {
21464                     newDate = new Date(this.date);
21465                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21466                     newViewDate = new Date(this.viewDate);
21467                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21468                 }
21469                 if (this.dateWithinRange(newDate)){
21470                     this.date = newDate;
21471                     this.viewDate = newViewDate;
21472                     this.setValue(this.formatDate(this.date));
21473 //                    this.update();
21474                     e.preventDefault();
21475                     dateChanged = true;
21476                 }
21477                 break;
21478             case 38: // up
21479             case 40: // down
21480                 if (!this.keyboardNavigation) {
21481                     break;
21482                 }
21483                 dir = e.keyCode == 38 ? -1 : 1;
21484                 if (e.ctrlKey){
21485                     newDate = this.moveYear(this.date, dir);
21486                     newViewDate = this.moveYear(this.viewDate, dir);
21487                 } else if (e.shiftKey){
21488                     newDate = this.moveMonth(this.date, dir);
21489                     newViewDate = this.moveMonth(this.viewDate, dir);
21490                 } else {
21491                     newDate = new Date(this.date);
21492                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21493                     newViewDate = new Date(this.viewDate);
21494                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21495                 }
21496                 if (this.dateWithinRange(newDate)){
21497                     this.date = newDate;
21498                     this.viewDate = newViewDate;
21499                     this.setValue(this.formatDate(this.date));
21500 //                    this.update();
21501                     e.preventDefault();
21502                     dateChanged = true;
21503                 }
21504                 break;
21505             case 13: // enter
21506                 this.setValue(this.formatDate(this.date));
21507                 this.hidePopup();
21508                 e.preventDefault();
21509                 break;
21510             case 9: // tab
21511                 this.setValue(this.formatDate(this.date));
21512                 this.hidePopup();
21513                 break;
21514             case 16: // shift
21515             case 17: // ctrl
21516             case 18: // alt
21517                 break;
21518             default :
21519                 this.hidePopup();
21520                 
21521         }
21522     },
21523     
21524     
21525     onClick: function(e) 
21526     {
21527         e.stopPropagation();
21528         e.preventDefault();
21529         
21530         var target = e.getTarget();
21531         
21532         if(target.nodeName.toLowerCase() === 'i'){
21533             target = Roo.get(target).dom.parentNode;
21534         }
21535         
21536         var nodeName = target.nodeName;
21537         var className = target.className;
21538         var html = target.innerHTML;
21539         //Roo.log(nodeName);
21540         
21541         switch(nodeName.toLowerCase()) {
21542             case 'th':
21543                 switch(className) {
21544                     case 'switch':
21545                         this.showMode(1);
21546                         break;
21547                     case 'prev':
21548                     case 'next':
21549                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21550                         switch(this.viewMode){
21551                                 case 0:
21552                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21553                                         break;
21554                                 case 1:
21555                                 case 2:
21556                                         this.viewDate = this.moveYear(this.viewDate, dir);
21557                                         break;
21558                         }
21559                         this.fill();
21560                         break;
21561                     case 'today':
21562                         var date = new Date();
21563                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21564 //                        this.fill()
21565                         this.setValue(this.formatDate(this.date));
21566                         
21567                         this.hidePopup();
21568                         break;
21569                 }
21570                 break;
21571             case 'span':
21572                 if (className.indexOf('disabled') < 0) {
21573                     this.viewDate.setUTCDate(1);
21574                     if (className.indexOf('month') > -1) {
21575                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21576                     } else {
21577                         var year = parseInt(html, 10) || 0;
21578                         this.viewDate.setUTCFullYear(year);
21579                         
21580                     }
21581                     
21582                     if(this.singleMode){
21583                         this.setValue(this.formatDate(this.viewDate));
21584                         this.hidePopup();
21585                         return;
21586                     }
21587                     
21588                     this.showMode(-1);
21589                     this.fill();
21590                 }
21591                 break;
21592                 
21593             case 'td':
21594                 //Roo.log(className);
21595                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21596                     var day = parseInt(html, 10) || 1;
21597                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21598                         month = (this.viewDate || new Date()).getUTCMonth();
21599
21600                     if (className.indexOf('old') > -1) {
21601                         if(month === 0 ){
21602                             month = 11;
21603                             year -= 1;
21604                         }else{
21605                             month -= 1;
21606                         }
21607                     } else if (className.indexOf('new') > -1) {
21608                         if (month == 11) {
21609                             month = 0;
21610                             year += 1;
21611                         } else {
21612                             month += 1;
21613                         }
21614                     }
21615                     //Roo.log([year,month,day]);
21616                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21617                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21618 //                    this.fill();
21619                     //Roo.log(this.formatDate(this.date));
21620                     this.setValue(this.formatDate(this.date));
21621                     this.hidePopup();
21622                 }
21623                 break;
21624         }
21625     },
21626     
21627     setStartDate: function(startDate)
21628     {
21629         this.startDate = startDate || -Infinity;
21630         if (this.startDate !== -Infinity) {
21631             this.startDate = this.parseDate(this.startDate);
21632         }
21633         this.update();
21634         this.updateNavArrows();
21635     },
21636
21637     setEndDate: function(endDate)
21638     {
21639         this.endDate = endDate || Infinity;
21640         if (this.endDate !== Infinity) {
21641             this.endDate = this.parseDate(this.endDate);
21642         }
21643         this.update();
21644         this.updateNavArrows();
21645     },
21646     
21647     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21648     {
21649         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21650         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21651             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21652         }
21653         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21654             return parseInt(d, 10);
21655         });
21656         this.update();
21657         this.updateNavArrows();
21658     },
21659     
21660     updateNavArrows: function() 
21661     {
21662         if(this.singleMode){
21663             return;
21664         }
21665         
21666         var d = new Date(this.viewDate),
21667         year = d.getUTCFullYear(),
21668         month = d.getUTCMonth();
21669         
21670         Roo.each(this.picker().select('.prev', true).elements, function(v){
21671             v.show();
21672             switch (this.viewMode) {
21673                 case 0:
21674
21675                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21676                         v.hide();
21677                     }
21678                     break;
21679                 case 1:
21680                 case 2:
21681                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21682                         v.hide();
21683                     }
21684                     break;
21685             }
21686         });
21687         
21688         Roo.each(this.picker().select('.next', true).elements, function(v){
21689             v.show();
21690             switch (this.viewMode) {
21691                 case 0:
21692
21693                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21694                         v.hide();
21695                     }
21696                     break;
21697                 case 1:
21698                 case 2:
21699                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21700                         v.hide();
21701                     }
21702                     break;
21703             }
21704         })
21705     },
21706     
21707     moveMonth: function(date, dir)
21708     {
21709         if (!dir) {
21710             return date;
21711         }
21712         var new_date = new Date(date.valueOf()),
21713         day = new_date.getUTCDate(),
21714         month = new_date.getUTCMonth(),
21715         mag = Math.abs(dir),
21716         new_month, test;
21717         dir = dir > 0 ? 1 : -1;
21718         if (mag == 1){
21719             test = dir == -1
21720             // If going back one month, make sure month is not current month
21721             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21722             ? function(){
21723                 return new_date.getUTCMonth() == month;
21724             }
21725             // If going forward one month, make sure month is as expected
21726             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21727             : function(){
21728                 return new_date.getUTCMonth() != new_month;
21729             };
21730             new_month = month + dir;
21731             new_date.setUTCMonth(new_month);
21732             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21733             if (new_month < 0 || new_month > 11) {
21734                 new_month = (new_month + 12) % 12;
21735             }
21736         } else {
21737             // For magnitudes >1, move one month at a time...
21738             for (var i=0; i<mag; i++) {
21739                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21740                 new_date = this.moveMonth(new_date, dir);
21741             }
21742             // ...then reset the day, keeping it in the new month
21743             new_month = new_date.getUTCMonth();
21744             new_date.setUTCDate(day);
21745             test = function(){
21746                 return new_month != new_date.getUTCMonth();
21747             };
21748         }
21749         // Common date-resetting loop -- if date is beyond end of month, make it
21750         // end of month
21751         while (test()){
21752             new_date.setUTCDate(--day);
21753             new_date.setUTCMonth(new_month);
21754         }
21755         return new_date;
21756     },
21757
21758     moveYear: function(date, dir)
21759     {
21760         return this.moveMonth(date, dir*12);
21761     },
21762
21763     dateWithinRange: function(date)
21764     {
21765         return date >= this.startDate && date <= this.endDate;
21766     },
21767
21768     
21769     remove: function() 
21770     {
21771         this.picker().remove();
21772     },
21773     
21774     validateValue : function(value)
21775     {
21776         if(this.getVisibilityEl().hasClass('hidden')){
21777             return true;
21778         }
21779         
21780         if(value.length < 1)  {
21781             if(this.allowBlank){
21782                 return true;
21783             }
21784             return false;
21785         }
21786         
21787         if(value.length < this.minLength){
21788             return false;
21789         }
21790         if(value.length > this.maxLength){
21791             return false;
21792         }
21793         if(this.vtype){
21794             var vt = Roo.form.VTypes;
21795             if(!vt[this.vtype](value, this)){
21796                 return false;
21797             }
21798         }
21799         if(typeof this.validator == "function"){
21800             var msg = this.validator(value);
21801             if(msg !== true){
21802                 return false;
21803             }
21804         }
21805         
21806         if(this.regex && !this.regex.test(value)){
21807             return false;
21808         }
21809         
21810         if(typeof(this.parseDate(value)) == 'undefined'){
21811             return false;
21812         }
21813         
21814         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21815             return false;
21816         }      
21817         
21818         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21819             return false;
21820         } 
21821         
21822         
21823         return true;
21824     },
21825     
21826     reset : function()
21827     {
21828         this.date = this.viewDate = '';
21829         
21830         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21831     }
21832    
21833 });
21834
21835 Roo.apply(Roo.bootstrap.DateField,  {
21836     
21837     head : {
21838         tag: 'thead',
21839         cn: [
21840         {
21841             tag: 'tr',
21842             cn: [
21843             {
21844                 tag: 'th',
21845                 cls: 'prev',
21846                 html: '<i class="fa fa-arrow-left"/>'
21847             },
21848             {
21849                 tag: 'th',
21850                 cls: 'switch',
21851                 colspan: '5'
21852             },
21853             {
21854                 tag: 'th',
21855                 cls: 'next',
21856                 html: '<i class="fa fa-arrow-right"/>'
21857             }
21858
21859             ]
21860         }
21861         ]
21862     },
21863     
21864     content : {
21865         tag: 'tbody',
21866         cn: [
21867         {
21868             tag: 'tr',
21869             cn: [
21870             {
21871                 tag: 'td',
21872                 colspan: '7'
21873             }
21874             ]
21875         }
21876         ]
21877     },
21878     
21879     footer : {
21880         tag: 'tfoot',
21881         cn: [
21882         {
21883             tag: 'tr',
21884             cn: [
21885             {
21886                 tag: 'th',
21887                 colspan: '7',
21888                 cls: 'today'
21889             }
21890                     
21891             ]
21892         }
21893         ]
21894     },
21895     
21896     dates:{
21897         en: {
21898             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21899             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21900             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21901             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21902             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21903             today: "Today"
21904         }
21905     },
21906     
21907     modes: [
21908     {
21909         clsName: 'days',
21910         navFnc: 'Month',
21911         navStep: 1
21912     },
21913     {
21914         clsName: 'months',
21915         navFnc: 'FullYear',
21916         navStep: 1
21917     },
21918     {
21919         clsName: 'years',
21920         navFnc: 'FullYear',
21921         navStep: 10
21922     }]
21923 });
21924
21925 Roo.apply(Roo.bootstrap.DateField,  {
21926   
21927     template : {
21928         tag: 'div',
21929         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21930         cn: [
21931         {
21932             tag: 'div',
21933             cls: 'datepicker-days',
21934             cn: [
21935             {
21936                 tag: 'table',
21937                 cls: 'table-condensed',
21938                 cn:[
21939                 Roo.bootstrap.DateField.head,
21940                 {
21941                     tag: 'tbody'
21942                 },
21943                 Roo.bootstrap.DateField.footer
21944                 ]
21945             }
21946             ]
21947         },
21948         {
21949             tag: 'div',
21950             cls: 'datepicker-months',
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             tag: 'div',
21965             cls: 'datepicker-years',
21966             cn: [
21967             {
21968                 tag: 'table',
21969                 cls: 'table-condensed',
21970                 cn:[
21971                 Roo.bootstrap.DateField.head,
21972                 Roo.bootstrap.DateField.content,
21973                 Roo.bootstrap.DateField.footer
21974                 ]
21975             }
21976             ]
21977         }
21978         ]
21979     }
21980 });
21981
21982  
21983
21984  /*
21985  * - LGPL
21986  *
21987  * TimeField
21988  * 
21989  */
21990
21991 /**
21992  * @class Roo.bootstrap.TimeField
21993  * @extends Roo.bootstrap.Input
21994  * Bootstrap DateField class
21995  * 
21996  * 
21997  * @constructor
21998  * Create a new TimeField
21999  * @param {Object} config The config object
22000  */
22001
22002 Roo.bootstrap.TimeField = function(config){
22003     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22004     this.addEvents({
22005             /**
22006              * @event show
22007              * Fires when this field show.
22008              * @param {Roo.bootstrap.DateField} thisthis
22009              * @param {Mixed} date The date value
22010              */
22011             show : true,
22012             /**
22013              * @event show
22014              * Fires when this field hide.
22015              * @param {Roo.bootstrap.DateField} this
22016              * @param {Mixed} date The date value
22017              */
22018             hide : true,
22019             /**
22020              * @event select
22021              * Fires when select a date.
22022              * @param {Roo.bootstrap.DateField} this
22023              * @param {Mixed} date The date value
22024              */
22025             select : true
22026         });
22027 };
22028
22029 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22030     
22031     /**
22032      * @cfg {String} format
22033      * The default time format string which can be overriden for localization support.  The format must be
22034      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22035      */
22036     format : "H:i",
22037
22038     getAutoCreate : function()
22039     {
22040         this.after = '<i class="fa far fa-clock"></i>';
22041         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22042         
22043          
22044     },
22045     onRender: function(ct, position)
22046     {
22047         
22048         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22049                 
22050         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22051         
22052         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22053         
22054         this.pop = this.picker().select('>.datepicker-time',true).first();
22055         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22056         
22057         this.picker().on('mousedown', this.onMousedown, this);
22058         this.picker().on('click', this.onClick, this);
22059         
22060         this.picker().addClass('datepicker-dropdown');
22061     
22062         this.fillTime();
22063         this.update();
22064             
22065         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22066         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22067         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22068         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22069         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22070         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22071
22072     },
22073     
22074     fireKey: function(e){
22075         if (!this.picker().isVisible()){
22076             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22077                 this.show();
22078             }
22079             return;
22080         }
22081
22082         e.preventDefault();
22083         
22084         switch(e.keyCode){
22085             case 27: // escape
22086                 this.hide();
22087                 break;
22088             case 37: // left
22089             case 39: // right
22090                 this.onTogglePeriod();
22091                 break;
22092             case 38: // up
22093                 this.onIncrementMinutes();
22094                 break;
22095             case 40: // down
22096                 this.onDecrementMinutes();
22097                 break;
22098             case 13: // enter
22099             case 9: // tab
22100                 this.setTime();
22101                 break;
22102         }
22103     },
22104     
22105     onClick: function(e) {
22106         e.stopPropagation();
22107         e.preventDefault();
22108     },
22109     
22110     picker : function()
22111     {
22112         return this.pickerEl;
22113     },
22114     
22115     fillTime: function()
22116     {    
22117         var time = this.pop.select('tbody', true).first();
22118         
22119         time.dom.innerHTML = '';
22120         
22121         time.createChild({
22122             tag: 'tr',
22123             cn: [
22124                 {
22125                     tag: 'td',
22126                     cn: [
22127                         {
22128                             tag: 'a',
22129                             href: '#',
22130                             cls: 'btn',
22131                             cn: [
22132                                 {
22133                                     tag: 'i',
22134                                     cls: 'hours-up fa fas fa-chevron-up'
22135                                 }
22136                             ]
22137                         } 
22138                     ]
22139                 },
22140                 {
22141                     tag: 'td',
22142                     cls: 'separator'
22143                 },
22144                 {
22145                     tag: 'td',
22146                     cn: [
22147                         {
22148                             tag: 'a',
22149                             href: '#',
22150                             cls: 'btn',
22151                             cn: [
22152                                 {
22153                                     tag: 'i',
22154                                     cls: 'minutes-up fa fas fa-chevron-up'
22155                                 }
22156                             ]
22157                         }
22158                     ]
22159                 },
22160                 {
22161                     tag: 'td',
22162                     cls: 'separator'
22163                 }
22164             ]
22165         });
22166         
22167         time.createChild({
22168             tag: 'tr',
22169             cn: [
22170                 {
22171                     tag: 'td',
22172                     cn: [
22173                         {
22174                             tag: 'span',
22175                             cls: 'timepicker-hour',
22176                             html: '00'
22177                         }  
22178                     ]
22179                 },
22180                 {
22181                     tag: 'td',
22182                     cls: 'separator',
22183                     html: ':'
22184                 },
22185                 {
22186                     tag: 'td',
22187                     cn: [
22188                         {
22189                             tag: 'span',
22190                             cls: 'timepicker-minute',
22191                             html: '00'
22192                         }  
22193                     ]
22194                 },
22195                 {
22196                     tag: 'td',
22197                     cls: 'separator'
22198                 },
22199                 {
22200                     tag: 'td',
22201                     cn: [
22202                         {
22203                             tag: 'button',
22204                             type: 'button',
22205                             cls: 'btn btn-primary period',
22206                             html: 'AM'
22207                             
22208                         }
22209                     ]
22210                 }
22211             ]
22212         });
22213         
22214         time.createChild({
22215             tag: 'tr',
22216             cn: [
22217                 {
22218                     tag: 'td',
22219                     cn: [
22220                         {
22221                             tag: 'a',
22222                             href: '#',
22223                             cls: 'btn',
22224                             cn: [
22225                                 {
22226                                     tag: 'span',
22227                                     cls: 'hours-down fa fas fa-chevron-down'
22228                                 }
22229                             ]
22230                         }
22231                     ]
22232                 },
22233                 {
22234                     tag: 'td',
22235                     cls: 'separator'
22236                 },
22237                 {
22238                     tag: 'td',
22239                     cn: [
22240                         {
22241                             tag: 'a',
22242                             href: '#',
22243                             cls: 'btn',
22244                             cn: [
22245                                 {
22246                                     tag: 'span',
22247                                     cls: 'minutes-down fa fas fa-chevron-down'
22248                                 }
22249                             ]
22250                         }
22251                     ]
22252                 },
22253                 {
22254                     tag: 'td',
22255                     cls: 'separator'
22256                 }
22257             ]
22258         });
22259         
22260     },
22261     
22262     update: function()
22263     {
22264         
22265         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22266         
22267         this.fill();
22268     },
22269     
22270     fill: function() 
22271     {
22272         var hours = this.time.getHours();
22273         var minutes = this.time.getMinutes();
22274         var period = 'AM';
22275         
22276         if(hours > 11){
22277             period = 'PM';
22278         }
22279         
22280         if(hours == 0){
22281             hours = 12;
22282         }
22283         
22284         
22285         if(hours > 12){
22286             hours = hours - 12;
22287         }
22288         
22289         if(hours < 10){
22290             hours = '0' + hours;
22291         }
22292         
22293         if(minutes < 10){
22294             minutes = '0' + minutes;
22295         }
22296         
22297         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22298         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22299         this.pop.select('button', true).first().dom.innerHTML = period;
22300         
22301     },
22302     
22303     place: function()
22304     {   
22305         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22306         
22307         var cls = ['bottom'];
22308         
22309         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22310             cls.pop();
22311             cls.push('top');
22312         }
22313         
22314         cls.push('right');
22315         
22316         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22317             cls.pop();
22318             cls.push('left');
22319         }
22320         //this.picker().setXY(20000,20000);
22321         this.picker().addClass(cls.join('-'));
22322         
22323         var _this = this;
22324         
22325         Roo.each(cls, function(c){
22326             if(c == 'bottom'){
22327                 (function() {
22328                  //  
22329                 }).defer(200);
22330                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22331                 //_this.picker().setTop(_this.inputEl().getHeight());
22332                 return;
22333             }
22334             if(c == 'top'){
22335                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22336                 
22337                 //_this.picker().setTop(0 - _this.picker().getHeight());
22338                 return;
22339             }
22340             /*
22341             if(c == 'left'){
22342                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22343                 return;
22344             }
22345             if(c == 'right'){
22346                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22347                 return;
22348             }
22349             */
22350         });
22351         
22352     },
22353   
22354     onFocus : function()
22355     {
22356         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22357         this.show();
22358     },
22359     
22360     onBlur : function()
22361     {
22362         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22363         this.hide();
22364     },
22365     
22366     show : function()
22367     {
22368         this.picker().show();
22369         this.pop.show();
22370         this.update();
22371         this.place();
22372         
22373         this.fireEvent('show', this, this.date);
22374     },
22375     
22376     hide : function()
22377     {
22378         this.picker().hide();
22379         this.pop.hide();
22380         
22381         this.fireEvent('hide', this, this.date);
22382     },
22383     
22384     setTime : function()
22385     {
22386         this.hide();
22387         this.setValue(this.time.format(this.format));
22388         
22389         this.fireEvent('select', this, this.date);
22390         
22391         
22392     },
22393     
22394     onMousedown: function(e){
22395         e.stopPropagation();
22396         e.preventDefault();
22397     },
22398     
22399     onIncrementHours: function()
22400     {
22401         Roo.log('onIncrementHours');
22402         this.time = this.time.add(Date.HOUR, 1);
22403         this.update();
22404         
22405     },
22406     
22407     onDecrementHours: function()
22408     {
22409         Roo.log('onDecrementHours');
22410         this.time = this.time.add(Date.HOUR, -1);
22411         this.update();
22412     },
22413     
22414     onIncrementMinutes: function()
22415     {
22416         Roo.log('onIncrementMinutes');
22417         this.time = this.time.add(Date.MINUTE, 1);
22418         this.update();
22419     },
22420     
22421     onDecrementMinutes: function()
22422     {
22423         Roo.log('onDecrementMinutes');
22424         this.time = this.time.add(Date.MINUTE, -1);
22425         this.update();
22426     },
22427     
22428     onTogglePeriod: function()
22429     {
22430         Roo.log('onTogglePeriod');
22431         this.time = this.time.add(Date.HOUR, 12);
22432         this.update();
22433     }
22434     
22435    
22436 });
22437  
22438
22439 Roo.apply(Roo.bootstrap.TimeField,  {
22440   
22441     template : {
22442         tag: 'div',
22443         cls: 'datepicker dropdown-menu',
22444         cn: [
22445             {
22446                 tag: 'div',
22447                 cls: 'datepicker-time',
22448                 cn: [
22449                 {
22450                     tag: 'table',
22451                     cls: 'table-condensed',
22452                     cn:[
22453                         {
22454                             tag: 'tbody',
22455                             cn: [
22456                                 {
22457                                     tag: 'tr',
22458                                     cn: [
22459                                     {
22460                                         tag: 'td',
22461                                         colspan: '7'
22462                                     }
22463                                     ]
22464                                 }
22465                             ]
22466                         },
22467                         {
22468                             tag: 'tfoot',
22469                             cn: [
22470                                 {
22471                                     tag: 'tr',
22472                                     cn: [
22473                                     {
22474                                         tag: 'th',
22475                                         colspan: '7',
22476                                         cls: '',
22477                                         cn: [
22478                                             {
22479                                                 tag: 'button',
22480                                                 cls: 'btn btn-info ok',
22481                                                 html: 'OK'
22482                                             }
22483                                         ]
22484                                     }
22485                     
22486                                     ]
22487                                 }
22488                             ]
22489                         }
22490                     ]
22491                 }
22492                 ]
22493             }
22494         ]
22495     }
22496 });
22497
22498  
22499
22500  /*
22501  * - LGPL
22502  *
22503  * MonthField
22504  * 
22505  */
22506
22507 /**
22508  * @class Roo.bootstrap.MonthField
22509  * @extends Roo.bootstrap.Input
22510  * Bootstrap MonthField class
22511  * 
22512  * @cfg {String} language default en
22513  * 
22514  * @constructor
22515  * Create a new MonthField
22516  * @param {Object} config The config object
22517  */
22518
22519 Roo.bootstrap.MonthField = function(config){
22520     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22521     
22522     this.addEvents({
22523         /**
22524          * @event show
22525          * Fires when this field show.
22526          * @param {Roo.bootstrap.MonthField} this
22527          * @param {Mixed} date The date value
22528          */
22529         show : true,
22530         /**
22531          * @event show
22532          * Fires when this field hide.
22533          * @param {Roo.bootstrap.MonthField} this
22534          * @param {Mixed} date The date value
22535          */
22536         hide : true,
22537         /**
22538          * @event select
22539          * Fires when select a date.
22540          * @param {Roo.bootstrap.MonthField} this
22541          * @param {String} oldvalue The old value
22542          * @param {String} newvalue The new value
22543          */
22544         select : true
22545     });
22546 };
22547
22548 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22549     
22550     onRender: function(ct, position)
22551     {
22552         
22553         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22554         
22555         this.language = this.language || 'en';
22556         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22557         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22558         
22559         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22560         this.isInline = false;
22561         this.isInput = true;
22562         this.component = this.el.select('.add-on', true).first() || false;
22563         this.component = (this.component && this.component.length === 0) ? false : this.component;
22564         this.hasInput = this.component && this.inputEL().length;
22565         
22566         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22567         
22568         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22569         
22570         this.picker().on('mousedown', this.onMousedown, this);
22571         this.picker().on('click', this.onClick, this);
22572         
22573         this.picker().addClass('datepicker-dropdown');
22574         
22575         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22576             v.setStyle('width', '189px');
22577         });
22578         
22579         this.fillMonths();
22580         
22581         this.update();
22582         
22583         if(this.isInline) {
22584             this.show();
22585         }
22586         
22587     },
22588     
22589     setValue: function(v, suppressEvent)
22590     {   
22591         var o = this.getValue();
22592         
22593         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22594         
22595         this.update();
22596
22597         if(suppressEvent !== true){
22598             this.fireEvent('select', this, o, v);
22599         }
22600         
22601     },
22602     
22603     getValue: function()
22604     {
22605         return this.value;
22606     },
22607     
22608     onClick: function(e) 
22609     {
22610         e.stopPropagation();
22611         e.preventDefault();
22612         
22613         var target = e.getTarget();
22614         
22615         if(target.nodeName.toLowerCase() === 'i'){
22616             target = Roo.get(target).dom.parentNode;
22617         }
22618         
22619         var nodeName = target.nodeName;
22620         var className = target.className;
22621         var html = target.innerHTML;
22622         
22623         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22624             return;
22625         }
22626         
22627         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22628         
22629         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22630         
22631         this.hide();
22632                         
22633     },
22634     
22635     picker : function()
22636     {
22637         return this.pickerEl;
22638     },
22639     
22640     fillMonths: function()
22641     {    
22642         var i = 0;
22643         var months = this.picker().select('>.datepicker-months td', true).first();
22644         
22645         months.dom.innerHTML = '';
22646         
22647         while (i < 12) {
22648             var month = {
22649                 tag: 'span',
22650                 cls: 'month',
22651                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22652             };
22653             
22654             months.createChild(month);
22655         }
22656         
22657     },
22658     
22659     update: function()
22660     {
22661         var _this = this;
22662         
22663         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22664             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22665         }
22666         
22667         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22668             e.removeClass('active');
22669             
22670             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22671                 e.addClass('active');
22672             }
22673         })
22674     },
22675     
22676     place: function()
22677     {
22678         if(this.isInline) {
22679             return;
22680         }
22681         
22682         this.picker().removeClass(['bottom', 'top']);
22683         
22684         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22685             /*
22686              * place to the top of element!
22687              *
22688              */
22689             
22690             this.picker().addClass('top');
22691             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22692             
22693             return;
22694         }
22695         
22696         this.picker().addClass('bottom');
22697         
22698         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22699     },
22700     
22701     onFocus : function()
22702     {
22703         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22704         this.show();
22705     },
22706     
22707     onBlur : function()
22708     {
22709         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22710         
22711         var d = this.inputEl().getValue();
22712         
22713         this.setValue(d);
22714                 
22715         this.hide();
22716     },
22717     
22718     show : function()
22719     {
22720         this.picker().show();
22721         this.picker().select('>.datepicker-months', true).first().show();
22722         this.update();
22723         this.place();
22724         
22725         this.fireEvent('show', this, this.date);
22726     },
22727     
22728     hide : function()
22729     {
22730         if(this.isInline) {
22731             return;
22732         }
22733         this.picker().hide();
22734         this.fireEvent('hide', this, this.date);
22735         
22736     },
22737     
22738     onMousedown: function(e)
22739     {
22740         e.stopPropagation();
22741         e.preventDefault();
22742     },
22743     
22744     keyup: function(e)
22745     {
22746         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22747         this.update();
22748     },
22749
22750     fireKey: function(e)
22751     {
22752         if (!this.picker().isVisible()){
22753             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22754                 this.show();
22755             }
22756             return;
22757         }
22758         
22759         var dir;
22760         
22761         switch(e.keyCode){
22762             case 27: // escape
22763                 this.hide();
22764                 e.preventDefault();
22765                 break;
22766             case 37: // left
22767             case 39: // right
22768                 dir = e.keyCode == 37 ? -1 : 1;
22769                 
22770                 this.vIndex = this.vIndex + dir;
22771                 
22772                 if(this.vIndex < 0){
22773                     this.vIndex = 0;
22774                 }
22775                 
22776                 if(this.vIndex > 11){
22777                     this.vIndex = 11;
22778                 }
22779                 
22780                 if(isNaN(this.vIndex)){
22781                     this.vIndex = 0;
22782                 }
22783                 
22784                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22785                 
22786                 break;
22787             case 38: // up
22788             case 40: // down
22789                 
22790                 dir = e.keyCode == 38 ? -1 : 1;
22791                 
22792                 this.vIndex = this.vIndex + dir * 4;
22793                 
22794                 if(this.vIndex < 0){
22795                     this.vIndex = 0;
22796                 }
22797                 
22798                 if(this.vIndex > 11){
22799                     this.vIndex = 11;
22800                 }
22801                 
22802                 if(isNaN(this.vIndex)){
22803                     this.vIndex = 0;
22804                 }
22805                 
22806                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22807                 break;
22808                 
22809             case 13: // enter
22810                 
22811                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22812                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22813                 }
22814                 
22815                 this.hide();
22816                 e.preventDefault();
22817                 break;
22818             case 9: // tab
22819                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22820                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22821                 }
22822                 this.hide();
22823                 break;
22824             case 16: // shift
22825             case 17: // ctrl
22826             case 18: // alt
22827                 break;
22828             default :
22829                 this.hide();
22830                 
22831         }
22832     },
22833     
22834     remove: function() 
22835     {
22836         this.picker().remove();
22837     }
22838    
22839 });
22840
22841 Roo.apply(Roo.bootstrap.MonthField,  {
22842     
22843     content : {
22844         tag: 'tbody',
22845         cn: [
22846         {
22847             tag: 'tr',
22848             cn: [
22849             {
22850                 tag: 'td',
22851                 colspan: '7'
22852             }
22853             ]
22854         }
22855         ]
22856     },
22857     
22858     dates:{
22859         en: {
22860             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22861             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22862         }
22863     }
22864 });
22865
22866 Roo.apply(Roo.bootstrap.MonthField,  {
22867   
22868     template : {
22869         tag: 'div',
22870         cls: 'datepicker dropdown-menu roo-dynamic',
22871         cn: [
22872             {
22873                 tag: 'div',
22874                 cls: 'datepicker-months',
22875                 cn: [
22876                 {
22877                     tag: 'table',
22878                     cls: 'table-condensed',
22879                     cn:[
22880                         Roo.bootstrap.DateField.content
22881                     ]
22882                 }
22883                 ]
22884             }
22885         ]
22886     }
22887 });
22888
22889  
22890
22891  
22892  /*
22893  * - LGPL
22894  *
22895  * CheckBox
22896  * 
22897  */
22898
22899 /**
22900  * @class Roo.bootstrap.CheckBox
22901  * @extends Roo.bootstrap.Input
22902  * Bootstrap CheckBox class
22903  * 
22904  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22905  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22906  * @cfg {String} boxLabel The text that appears beside the checkbox
22907  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22908  * @cfg {Boolean} checked initnal the element
22909  * @cfg {Boolean} inline inline the element (default false)
22910  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22911  * @cfg {String} tooltip label tooltip
22912  * 
22913  * @constructor
22914  * Create a new CheckBox
22915  * @param {Object} config The config object
22916  */
22917
22918 Roo.bootstrap.CheckBox = function(config){
22919     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22920    
22921     this.addEvents({
22922         /**
22923         * @event check
22924         * Fires when the element is checked or unchecked.
22925         * @param {Roo.bootstrap.CheckBox} this This input
22926         * @param {Boolean} checked The new checked value
22927         */
22928        check : true,
22929        /**
22930         * @event click
22931         * Fires when the element is click.
22932         * @param {Roo.bootstrap.CheckBox} this This input
22933         */
22934        click : true
22935     });
22936     
22937 };
22938
22939 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22940   
22941     inputType: 'checkbox',
22942     inputValue: 1,
22943     valueOff: 0,
22944     boxLabel: false,
22945     checked: false,
22946     weight : false,
22947     inline: false,
22948     tooltip : '',
22949     
22950     // checkbox success does not make any sense really.. 
22951     invalidClass : "",
22952     validClass : "",
22953     
22954     
22955     getAutoCreate : function()
22956     {
22957         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22958         
22959         var id = Roo.id();
22960         
22961         var cfg = {};
22962         
22963         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22964         
22965         if(this.inline){
22966             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22967         }
22968         
22969         var input =  {
22970             tag: 'input',
22971             id : id,
22972             type : this.inputType,
22973             value : this.inputValue,
22974             cls : 'roo-' + this.inputType, //'form-box',
22975             placeholder : this.placeholder || ''
22976             
22977         };
22978         
22979         if(this.inputType != 'radio'){
22980             var hidden =  {
22981                 tag: 'input',
22982                 type : 'hidden',
22983                 cls : 'roo-hidden-value',
22984                 value : this.checked ? this.inputValue : this.valueOff
22985             };
22986         }
22987         
22988             
22989         if (this.weight) { // Validity check?
22990             cfg.cls += " " + this.inputType + "-" + this.weight;
22991         }
22992         
22993         if (this.disabled) {
22994             input.disabled=true;
22995         }
22996         
22997         if(this.checked){
22998             input.checked = this.checked;
22999         }
23000         
23001         if (this.name) {
23002             
23003             input.name = this.name;
23004             
23005             if(this.inputType != 'radio'){
23006                 hidden.name = this.name;
23007                 input.name = '_hidden_' + this.name;
23008             }
23009         }
23010         
23011         if (this.size) {
23012             input.cls += ' input-' + this.size;
23013         }
23014         
23015         var settings=this;
23016         
23017         ['xs','sm','md','lg'].map(function(size){
23018             if (settings[size]) {
23019                 cfg.cls += ' col-' + size + '-' + settings[size];
23020             }
23021         });
23022         
23023         var inputblock = input;
23024          
23025         if (this.before || this.after) {
23026             
23027             inputblock = {
23028                 cls : 'input-group',
23029                 cn :  [] 
23030             };
23031             
23032             if (this.before) {
23033                 inputblock.cn.push({
23034                     tag :'span',
23035                     cls : 'input-group-addon',
23036                     html : this.before
23037                 });
23038             }
23039             
23040             inputblock.cn.push(input);
23041             
23042             if(this.inputType != 'radio'){
23043                 inputblock.cn.push(hidden);
23044             }
23045             
23046             if (this.after) {
23047                 inputblock.cn.push({
23048                     tag :'span',
23049                     cls : 'input-group-addon',
23050                     html : this.after
23051                 });
23052             }
23053             
23054         }
23055         var boxLabelCfg = false;
23056         
23057         if(this.boxLabel){
23058            
23059             boxLabelCfg = {
23060                 tag: 'label',
23061                 //'for': id, // box label is handled by onclick - so no for...
23062                 cls: 'box-label',
23063                 html: this.boxLabel
23064             };
23065             if(this.tooltip){
23066                 boxLabelCfg.tooltip = this.tooltip;
23067             }
23068              
23069         }
23070         
23071         
23072         if (align ==='left' && this.fieldLabel.length) {
23073 //                Roo.log("left and has label");
23074             cfg.cn = [
23075                 {
23076                     tag: 'label',
23077                     'for' :  id,
23078                     cls : 'control-label',
23079                     html : this.fieldLabel
23080                 },
23081                 {
23082                     cls : "", 
23083                     cn: [
23084                         inputblock
23085                     ]
23086                 }
23087             ];
23088             
23089             if (boxLabelCfg) {
23090                 cfg.cn[1].cn.push(boxLabelCfg);
23091             }
23092             
23093             if(this.labelWidth > 12){
23094                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23095             }
23096             
23097             if(this.labelWidth < 13 && this.labelmd == 0){
23098                 this.labelmd = this.labelWidth;
23099             }
23100             
23101             if(this.labellg > 0){
23102                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23103                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23104             }
23105             
23106             if(this.labelmd > 0){
23107                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23108                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23109             }
23110             
23111             if(this.labelsm > 0){
23112                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23113                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23114             }
23115             
23116             if(this.labelxs > 0){
23117                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23118                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23119             }
23120             
23121         } else if ( this.fieldLabel.length) {
23122 //                Roo.log(" label");
23123                 cfg.cn = [
23124                    
23125                     {
23126                         tag: this.boxLabel ? 'span' : 'label',
23127                         'for': id,
23128                         cls: 'control-label box-input-label',
23129                         //cls : 'input-group-addon',
23130                         html : this.fieldLabel
23131                     },
23132                     
23133                     inputblock
23134                     
23135                 ];
23136                 if (boxLabelCfg) {
23137                     cfg.cn.push(boxLabelCfg);
23138                 }
23139
23140         } else {
23141             
23142 //                Roo.log(" no label && no align");
23143                 cfg.cn = [  inputblock ] ;
23144                 if (boxLabelCfg) {
23145                     cfg.cn.push(boxLabelCfg);
23146                 }
23147
23148                 
23149         }
23150         
23151        
23152         
23153         if(this.inputType != 'radio'){
23154             cfg.cn.push(hidden);
23155         }
23156         
23157         return cfg;
23158         
23159     },
23160     
23161     /**
23162      * return the real input element.
23163      */
23164     inputEl: function ()
23165     {
23166         return this.el.select('input.roo-' + this.inputType,true).first();
23167     },
23168     hiddenEl: function ()
23169     {
23170         return this.el.select('input.roo-hidden-value',true).first();
23171     },
23172     
23173     labelEl: function()
23174     {
23175         return this.el.select('label.control-label',true).first();
23176     },
23177     /* depricated... */
23178     
23179     label: function()
23180     {
23181         return this.labelEl();
23182     },
23183     
23184     boxLabelEl: function()
23185     {
23186         return this.el.select('label.box-label',true).first();
23187     },
23188     
23189     initEvents : function()
23190     {
23191 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23192         
23193         this.inputEl().on('click', this.onClick,  this);
23194         
23195         if (this.boxLabel) { 
23196             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23197         }
23198         
23199         this.startValue = this.getValue();
23200         
23201         if(this.groupId){
23202             Roo.bootstrap.CheckBox.register(this);
23203         }
23204     },
23205     
23206     onClick : function(e)
23207     {   
23208         if(this.fireEvent('click', this, e) !== false){
23209             this.setChecked(!this.checked);
23210         }
23211         
23212     },
23213     
23214     setChecked : function(state,suppressEvent)
23215     {
23216         this.startValue = this.getValue();
23217
23218         if(this.inputType == 'radio'){
23219             
23220             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23221                 e.dom.checked = false;
23222             });
23223             
23224             this.inputEl().dom.checked = true;
23225             
23226             this.inputEl().dom.value = this.inputValue;
23227             
23228             if(suppressEvent !== true){
23229                 this.fireEvent('check', this, true);
23230             }
23231             
23232             this.validate();
23233             
23234             return;
23235         }
23236         
23237         this.checked = state;
23238         
23239         this.inputEl().dom.checked = state;
23240         
23241         
23242         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23243         
23244         if(suppressEvent !== true){
23245             this.fireEvent('check', this, state);
23246         }
23247         
23248         this.validate();
23249     },
23250     
23251     getValue : function()
23252     {
23253         if(this.inputType == 'radio'){
23254             return this.getGroupValue();
23255         }
23256         
23257         return this.hiddenEl().dom.value;
23258         
23259     },
23260     
23261     getGroupValue : function()
23262     {
23263         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23264             return '';
23265         }
23266         
23267         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23268     },
23269     
23270     setValue : function(v,suppressEvent)
23271     {
23272         if(this.inputType == 'radio'){
23273             this.setGroupValue(v, suppressEvent);
23274             return;
23275         }
23276         
23277         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23278         
23279         this.validate();
23280     },
23281     
23282     setGroupValue : function(v, suppressEvent)
23283     {
23284         this.startValue = this.getValue();
23285         
23286         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23287             e.dom.checked = false;
23288             
23289             if(e.dom.value == v){
23290                 e.dom.checked = true;
23291             }
23292         });
23293         
23294         if(suppressEvent !== true){
23295             this.fireEvent('check', this, true);
23296         }
23297
23298         this.validate();
23299         
23300         return;
23301     },
23302     
23303     validate : function()
23304     {
23305         if(this.getVisibilityEl().hasClass('hidden')){
23306             return true;
23307         }
23308         
23309         if(
23310                 this.disabled || 
23311                 (this.inputType == 'radio' && this.validateRadio()) ||
23312                 (this.inputType == 'checkbox' && this.validateCheckbox())
23313         ){
23314             this.markValid();
23315             return true;
23316         }
23317         
23318         this.markInvalid();
23319         return false;
23320     },
23321     
23322     validateRadio : function()
23323     {
23324         if(this.getVisibilityEl().hasClass('hidden')){
23325             return true;
23326         }
23327         
23328         if(this.allowBlank){
23329             return true;
23330         }
23331         
23332         var valid = false;
23333         
23334         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23335             if(!e.dom.checked){
23336                 return;
23337             }
23338             
23339             valid = true;
23340             
23341             return false;
23342         });
23343         
23344         return valid;
23345     },
23346     
23347     validateCheckbox : function()
23348     {
23349         if(!this.groupId){
23350             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23351             //return (this.getValue() == this.inputValue) ? true : false;
23352         }
23353         
23354         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23355         
23356         if(!group){
23357             return false;
23358         }
23359         
23360         var r = false;
23361         
23362         for(var i in group){
23363             if(group[i].el.isVisible(true)){
23364                 r = false;
23365                 break;
23366             }
23367             
23368             r = true;
23369         }
23370         
23371         for(var i in group){
23372             if(r){
23373                 break;
23374             }
23375             
23376             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23377         }
23378         
23379         return r;
23380     },
23381     
23382     /**
23383      * Mark this field as valid
23384      */
23385     markValid : function()
23386     {
23387         var _this = this;
23388         
23389         this.fireEvent('valid', this);
23390         
23391         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23392         
23393         if(this.groupId){
23394             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23395         }
23396         
23397         if(label){
23398             label.markValid();
23399         }
23400
23401         if(this.inputType == 'radio'){
23402             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23403                 var fg = e.findParent('.form-group', false, true);
23404                 if (Roo.bootstrap.version == 3) {
23405                     fg.removeClass([_this.invalidClass, _this.validClass]);
23406                     fg.addClass(_this.validClass);
23407                 } else {
23408                     fg.removeClass(['is-valid', 'is-invalid']);
23409                     fg.addClass('is-valid');
23410                 }
23411             });
23412             
23413             return;
23414         }
23415
23416         if(!this.groupId){
23417             var fg = this.el.findParent('.form-group', false, true);
23418             if (Roo.bootstrap.version == 3) {
23419                 fg.removeClass([this.invalidClass, this.validClass]);
23420                 fg.addClass(this.validClass);
23421             } else {
23422                 fg.removeClass(['is-valid', 'is-invalid']);
23423                 fg.addClass('is-valid');
23424             }
23425             return;
23426         }
23427         
23428         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23429         
23430         if(!group){
23431             return;
23432         }
23433         
23434         for(var i in group){
23435             var fg = group[i].el.findParent('.form-group', false, true);
23436             if (Roo.bootstrap.version == 3) {
23437                 fg.removeClass([this.invalidClass, this.validClass]);
23438                 fg.addClass(this.validClass);
23439             } else {
23440                 fg.removeClass(['is-valid', 'is-invalid']);
23441                 fg.addClass('is-valid');
23442             }
23443         }
23444     },
23445     
23446      /**
23447      * Mark this field as invalid
23448      * @param {String} msg The validation message
23449      */
23450     markInvalid : function(msg)
23451     {
23452         if(this.allowBlank){
23453             return;
23454         }
23455         
23456         var _this = this;
23457         
23458         this.fireEvent('invalid', this, msg);
23459         
23460         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23461         
23462         if(this.groupId){
23463             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23464         }
23465         
23466         if(label){
23467             label.markInvalid();
23468         }
23469             
23470         if(this.inputType == 'radio'){
23471             
23472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23473                 var fg = e.findParent('.form-group', false, true);
23474                 if (Roo.bootstrap.version == 3) {
23475                     fg.removeClass([_this.invalidClass, _this.validClass]);
23476                     fg.addClass(_this.invalidClass);
23477                 } else {
23478                     fg.removeClass(['is-invalid', 'is-valid']);
23479                     fg.addClass('is-invalid');
23480                 }
23481             });
23482             
23483             return;
23484         }
23485         
23486         if(!this.groupId){
23487             var fg = this.el.findParent('.form-group', false, true);
23488             if (Roo.bootstrap.version == 3) {
23489                 fg.removeClass([_this.invalidClass, _this.validClass]);
23490                 fg.addClass(_this.invalidClass);
23491             } else {
23492                 fg.removeClass(['is-invalid', 'is-valid']);
23493                 fg.addClass('is-invalid');
23494             }
23495             return;
23496         }
23497         
23498         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23499         
23500         if(!group){
23501             return;
23502         }
23503         
23504         for(var i in group){
23505             var fg = group[i].el.findParent('.form-group', false, true);
23506             if (Roo.bootstrap.version == 3) {
23507                 fg.removeClass([_this.invalidClass, _this.validClass]);
23508                 fg.addClass(_this.invalidClass);
23509             } else {
23510                 fg.removeClass(['is-invalid', 'is-valid']);
23511                 fg.addClass('is-invalid');
23512             }
23513         }
23514         
23515     },
23516     
23517     clearInvalid : function()
23518     {
23519         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23520         
23521         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23522         
23523         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23524         
23525         if (label && label.iconEl) {
23526             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23527             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23528         }
23529     },
23530     
23531     disable : function()
23532     {
23533         if(this.inputType != 'radio'){
23534             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23535             return;
23536         }
23537         
23538         var _this = this;
23539         
23540         if(this.rendered){
23541             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23542                 _this.getActionEl().addClass(this.disabledClass);
23543                 e.dom.disabled = true;
23544             });
23545         }
23546         
23547         this.disabled = true;
23548         this.fireEvent("disable", this);
23549         return this;
23550     },
23551
23552     enable : function()
23553     {
23554         if(this.inputType != 'radio'){
23555             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23556             return;
23557         }
23558         
23559         var _this = this;
23560         
23561         if(this.rendered){
23562             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23563                 _this.getActionEl().removeClass(this.disabledClass);
23564                 e.dom.disabled = false;
23565             });
23566         }
23567         
23568         this.disabled = false;
23569         this.fireEvent("enable", this);
23570         return this;
23571     },
23572     
23573     setBoxLabel : function(v)
23574     {
23575         this.boxLabel = v;
23576         
23577         if(this.rendered){
23578             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23579         }
23580     }
23581
23582 });
23583
23584 Roo.apply(Roo.bootstrap.CheckBox, {
23585     
23586     groups: {},
23587     
23588      /**
23589     * register a CheckBox Group
23590     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23591     */
23592     register : function(checkbox)
23593     {
23594         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23595             this.groups[checkbox.groupId] = {};
23596         }
23597         
23598         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23599             return;
23600         }
23601         
23602         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23603         
23604     },
23605     /**
23606     * fetch a CheckBox Group based on the group ID
23607     * @param {string} the group ID
23608     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23609     */
23610     get: function(groupId) {
23611         if (typeof(this.groups[groupId]) == 'undefined') {
23612             return false;
23613         }
23614         
23615         return this.groups[groupId] ;
23616     }
23617     
23618     
23619 });
23620 /*
23621  * - LGPL
23622  *
23623  * RadioItem
23624  * 
23625  */
23626
23627 /**
23628  * @class Roo.bootstrap.Radio
23629  * @extends Roo.bootstrap.Component
23630  * Bootstrap Radio class
23631  * @cfg {String} boxLabel - the label associated
23632  * @cfg {String} value - the value of radio
23633  * 
23634  * @constructor
23635  * Create a new Radio
23636  * @param {Object} config The config object
23637  */
23638 Roo.bootstrap.Radio = function(config){
23639     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23640     
23641 };
23642
23643 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23644     
23645     boxLabel : '',
23646     
23647     value : '',
23648     
23649     getAutoCreate : function()
23650     {
23651         var cfg = {
23652             tag : 'div',
23653             cls : 'form-group radio',
23654             cn : [
23655                 {
23656                     tag : 'label',
23657                     cls : 'box-label',
23658                     html : this.boxLabel
23659                 }
23660             ]
23661         };
23662         
23663         return cfg;
23664     },
23665     
23666     initEvents : function() 
23667     {
23668         this.parent().register(this);
23669         
23670         this.el.on('click', this.onClick, this);
23671         
23672     },
23673     
23674     onClick : function(e)
23675     {
23676         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23677             this.setChecked(true);
23678         }
23679     },
23680     
23681     setChecked : function(state, suppressEvent)
23682     {
23683         this.parent().setValue(this.value, suppressEvent);
23684         
23685     },
23686     
23687     setBoxLabel : function(v)
23688     {
23689         this.boxLabel = v;
23690         
23691         if(this.rendered){
23692             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23693         }
23694     }
23695     
23696 });
23697  
23698
23699  /*
23700  * - LGPL
23701  *
23702  * Input
23703  * 
23704  */
23705
23706 /**
23707  * @class Roo.bootstrap.SecurePass
23708  * @extends Roo.bootstrap.Input
23709  * Bootstrap SecurePass class
23710  *
23711  * 
23712  * @constructor
23713  * Create a new SecurePass
23714  * @param {Object} config The config object
23715  */
23716  
23717 Roo.bootstrap.SecurePass = function (config) {
23718     // these go here, so the translation tool can replace them..
23719     this.errors = {
23720         PwdEmpty: "Please type a password, and then retype it to confirm.",
23721         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23722         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23723         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23724         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23725         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23726         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23727         TooWeak: "Your password is Too Weak."
23728     },
23729     this.meterLabel = "Password strength:";
23730     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23731     this.meterClass = [
23732         "roo-password-meter-tooweak", 
23733         "roo-password-meter-weak", 
23734         "roo-password-meter-medium", 
23735         "roo-password-meter-strong", 
23736         "roo-password-meter-grey"
23737     ];
23738     
23739     this.errors = {};
23740     
23741     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23742 }
23743
23744 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23745     /**
23746      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23747      * {
23748      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23749      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23750      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23751      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23752      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23753      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23754      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23755      * })
23756      */
23757     // private
23758     
23759     meterWidth: 300,
23760     errorMsg :'',    
23761     errors: false,
23762     imageRoot: '/',
23763     /**
23764      * @cfg {String/Object} Label for the strength meter (defaults to
23765      * 'Password strength:')
23766      */
23767     // private
23768     meterLabel: '',
23769     /**
23770      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23771      * ['Weak', 'Medium', 'Strong'])
23772      */
23773     // private    
23774     pwdStrengths: false,    
23775     // private
23776     strength: 0,
23777     // private
23778     _lastPwd: null,
23779     // private
23780     kCapitalLetter: 0,
23781     kSmallLetter: 1,
23782     kDigit: 2,
23783     kPunctuation: 3,
23784     
23785     insecure: false,
23786     // private
23787     initEvents: function ()
23788     {
23789         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23790
23791         if (this.el.is('input[type=password]') && Roo.isSafari) {
23792             this.el.on('keydown', this.SafariOnKeyDown, this);
23793         }
23794
23795         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23796     },
23797     // private
23798     onRender: function (ct, position)
23799     {
23800         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23801         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23802         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23803
23804         this.trigger.createChild({
23805                    cn: [
23806                     {
23807                     //id: 'PwdMeter',
23808                     tag: 'div',
23809                     cls: 'roo-password-meter-grey col-xs-12',
23810                     style: {
23811                         //width: 0,
23812                         //width: this.meterWidth + 'px'                                                
23813                         }
23814                     },
23815                     {                            
23816                          cls: 'roo-password-meter-text'                          
23817                     }
23818                 ]            
23819         });
23820
23821          
23822         if (this.hideTrigger) {
23823             this.trigger.setDisplayed(false);
23824         }
23825         this.setSize(this.width || '', this.height || '');
23826     },
23827     // private
23828     onDestroy: function ()
23829     {
23830         if (this.trigger) {
23831             this.trigger.removeAllListeners();
23832             this.trigger.remove();
23833         }
23834         if (this.wrap) {
23835             this.wrap.remove();
23836         }
23837         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23838     },
23839     // private
23840     checkStrength: function ()
23841     {
23842         var pwd = this.inputEl().getValue();
23843         if (pwd == this._lastPwd) {
23844             return;
23845         }
23846
23847         var strength;
23848         if (this.ClientSideStrongPassword(pwd)) {
23849             strength = 3;
23850         } else if (this.ClientSideMediumPassword(pwd)) {
23851             strength = 2;
23852         } else if (this.ClientSideWeakPassword(pwd)) {
23853             strength = 1;
23854         } else {
23855             strength = 0;
23856         }
23857         
23858         Roo.log('strength1: ' + strength);
23859         
23860         //var pm = this.trigger.child('div/div/div').dom;
23861         var pm = this.trigger.child('div/div');
23862         pm.removeClass(this.meterClass);
23863         pm.addClass(this.meterClass[strength]);
23864                 
23865         
23866         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23867                 
23868         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23869         
23870         this._lastPwd = pwd;
23871     },
23872     reset: function ()
23873     {
23874         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23875         
23876         this._lastPwd = '';
23877         
23878         var pm = this.trigger.child('div/div');
23879         pm.removeClass(this.meterClass);
23880         pm.addClass('roo-password-meter-grey');        
23881         
23882         
23883         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23884         
23885         pt.innerHTML = '';
23886         this.inputEl().dom.type='password';
23887     },
23888     // private
23889     validateValue: function (value)
23890     {
23891         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23892             return false;
23893         }
23894         if (value.length == 0) {
23895             if (this.allowBlank) {
23896                 this.clearInvalid();
23897                 return true;
23898             }
23899
23900             this.markInvalid(this.errors.PwdEmpty);
23901             this.errorMsg = this.errors.PwdEmpty;
23902             return false;
23903         }
23904         
23905         if(this.insecure){
23906             return true;
23907         }
23908         
23909         if (!value.match(/[\x21-\x7e]+/)) {
23910             this.markInvalid(this.errors.PwdBadChar);
23911             this.errorMsg = this.errors.PwdBadChar;
23912             return false;
23913         }
23914         if (value.length < 6) {
23915             this.markInvalid(this.errors.PwdShort);
23916             this.errorMsg = this.errors.PwdShort;
23917             return false;
23918         }
23919         if (value.length > 16) {
23920             this.markInvalid(this.errors.PwdLong);
23921             this.errorMsg = this.errors.PwdLong;
23922             return false;
23923         }
23924         var strength;
23925         if (this.ClientSideStrongPassword(value)) {
23926             strength = 3;
23927         } else if (this.ClientSideMediumPassword(value)) {
23928             strength = 2;
23929         } else if (this.ClientSideWeakPassword(value)) {
23930             strength = 1;
23931         } else {
23932             strength = 0;
23933         }
23934
23935         
23936         if (strength < 2) {
23937             //this.markInvalid(this.errors.TooWeak);
23938             this.errorMsg = this.errors.TooWeak;
23939             //return false;
23940         }
23941         
23942         
23943         console.log('strength2: ' + strength);
23944         
23945         //var pm = this.trigger.child('div/div/div').dom;
23946         
23947         var pm = this.trigger.child('div/div');
23948         pm.removeClass(this.meterClass);
23949         pm.addClass(this.meterClass[strength]);
23950                 
23951         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23952                 
23953         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23954         
23955         this.errorMsg = ''; 
23956         return true;
23957     },
23958     // private
23959     CharacterSetChecks: function (type)
23960     {
23961         this.type = type;
23962         this.fResult = false;
23963     },
23964     // private
23965     isctype: function (character, type)
23966     {
23967         switch (type) {  
23968             case this.kCapitalLetter:
23969                 if (character >= 'A' && character <= 'Z') {
23970                     return true;
23971                 }
23972                 break;
23973             
23974             case this.kSmallLetter:
23975                 if (character >= 'a' && character <= 'z') {
23976                     return true;
23977                 }
23978                 break;
23979             
23980             case this.kDigit:
23981                 if (character >= '0' && character <= '9') {
23982                     return true;
23983                 }
23984                 break;
23985             
23986             case this.kPunctuation:
23987                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23988                     return true;
23989                 }
23990                 break;
23991             
23992             default:
23993                 return false;
23994         }
23995
23996     },
23997     // private
23998     IsLongEnough: function (pwd, size)
23999     {
24000         return !(pwd == null || isNaN(size) || pwd.length < size);
24001     },
24002     // private
24003     SpansEnoughCharacterSets: function (word, nb)
24004     {
24005         if (!this.IsLongEnough(word, nb))
24006         {
24007             return false;
24008         }
24009
24010         var characterSetChecks = new Array(
24011             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24012             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24013         );
24014         
24015         for (var index = 0; index < word.length; ++index) {
24016             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24017                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24018                     characterSetChecks[nCharSet].fResult = true;
24019                     break;
24020                 }
24021             }
24022         }
24023
24024         var nCharSets = 0;
24025         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24026             if (characterSetChecks[nCharSet].fResult) {
24027                 ++nCharSets;
24028             }
24029         }
24030
24031         if (nCharSets < nb) {
24032             return false;
24033         }
24034         return true;
24035     },
24036     // private
24037     ClientSideStrongPassword: function (pwd)
24038     {
24039         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24040     },
24041     // private
24042     ClientSideMediumPassword: function (pwd)
24043     {
24044         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24045     },
24046     // private
24047     ClientSideWeakPassword: function (pwd)
24048     {
24049         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24050     }
24051           
24052 })//<script type="text/javascript">
24053
24054 /*
24055  * Based  Ext JS Library 1.1.1
24056  * Copyright(c) 2006-2007, Ext JS, LLC.
24057  * LGPL
24058  *
24059  */
24060  
24061 /**
24062  * @class Roo.HtmlEditorCore
24063  * @extends Roo.Component
24064  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24065  *
24066  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24067  */
24068
24069 Roo.HtmlEditorCore = function(config){
24070     
24071     
24072     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24073     
24074     
24075     this.addEvents({
24076         /**
24077          * @event initialize
24078          * Fires when the editor is fully initialized (including the iframe)
24079          * @param {Roo.HtmlEditorCore} this
24080          */
24081         initialize: true,
24082         /**
24083          * @event activate
24084          * Fires when the editor is first receives the focus. Any insertion must wait
24085          * until after this event.
24086          * @param {Roo.HtmlEditorCore} this
24087          */
24088         activate: true,
24089          /**
24090          * @event beforesync
24091          * Fires before the textarea is updated with content from the editor iframe. Return false
24092          * to cancel the sync.
24093          * @param {Roo.HtmlEditorCore} this
24094          * @param {String} html
24095          */
24096         beforesync: true,
24097          /**
24098          * @event beforepush
24099          * Fires before the iframe editor is updated with content from the textarea. Return false
24100          * to cancel the push.
24101          * @param {Roo.HtmlEditorCore} this
24102          * @param {String} html
24103          */
24104         beforepush: true,
24105          /**
24106          * @event sync
24107          * Fires when the textarea is updated with content from the editor iframe.
24108          * @param {Roo.HtmlEditorCore} this
24109          * @param {String} html
24110          */
24111         sync: true,
24112          /**
24113          * @event push
24114          * Fires when the iframe editor is updated with content from the textarea.
24115          * @param {Roo.HtmlEditorCore} this
24116          * @param {String} html
24117          */
24118         push: true,
24119         
24120         /**
24121          * @event editorevent
24122          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24123          * @param {Roo.HtmlEditorCore} this
24124          */
24125         editorevent: true
24126         
24127     });
24128     
24129     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24130     
24131     // defaults : white / black...
24132     this.applyBlacklists();
24133     
24134     
24135     
24136 };
24137
24138
24139 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24140
24141
24142      /**
24143      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24144      */
24145     
24146     owner : false,
24147     
24148      /**
24149      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24150      *                        Roo.resizable.
24151      */
24152     resizable : false,
24153      /**
24154      * @cfg {Number} height (in pixels)
24155      */   
24156     height: 300,
24157    /**
24158      * @cfg {Number} width (in pixels)
24159      */   
24160     width: 500,
24161     
24162     /**
24163      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24164      * 
24165      */
24166     stylesheets: false,
24167     
24168     // id of frame..
24169     frameId: false,
24170     
24171     // private properties
24172     validationEvent : false,
24173     deferHeight: true,
24174     initialized : false,
24175     activated : false,
24176     sourceEditMode : false,
24177     onFocus : Roo.emptyFn,
24178     iframePad:3,
24179     hideMode:'offsets',
24180     
24181     clearUp: true,
24182     
24183     // blacklist + whitelisted elements..
24184     black: false,
24185     white: false,
24186      
24187     bodyCls : '',
24188
24189     /**
24190      * Protected method that will not generally be called directly. It
24191      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24192      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24193      */
24194     getDocMarkup : function(){
24195         // body styles..
24196         var st = '';
24197         
24198         // inherit styels from page...?? 
24199         if (this.stylesheets === false) {
24200             
24201             Roo.get(document.head).select('style').each(function(node) {
24202                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24203             });
24204             
24205             Roo.get(document.head).select('link').each(function(node) { 
24206                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24207             });
24208             
24209         } else if (!this.stylesheets.length) {
24210                 // simple..
24211                 st = '<style type="text/css">' +
24212                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24213                    '</style>';
24214         } else {
24215             for (var i in this.stylesheets) { 
24216                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24217             }
24218             
24219         }
24220         
24221         st +=  '<style type="text/css">' +
24222             'IMG { cursor: pointer } ' +
24223         '</style>';
24224
24225         var cls = 'roo-htmleditor-body';
24226         
24227         if(this.bodyCls.length){
24228             cls += ' ' + this.bodyCls;
24229         }
24230         
24231         return '<html><head>' + st  +
24232             //<style type="text/css">' +
24233             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24234             //'</style>' +
24235             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24236     },
24237
24238     // private
24239     onRender : function(ct, position)
24240     {
24241         var _t = this;
24242         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24243         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24244         
24245         
24246         this.el.dom.style.border = '0 none';
24247         this.el.dom.setAttribute('tabIndex', -1);
24248         this.el.addClass('x-hidden hide');
24249         
24250         
24251         
24252         if(Roo.isIE){ // fix IE 1px bogus margin
24253             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24254         }
24255        
24256         
24257         this.frameId = Roo.id();
24258         
24259          
24260         
24261         var iframe = this.owner.wrap.createChild({
24262             tag: 'iframe',
24263             cls: 'form-control', // bootstrap..
24264             id: this.frameId,
24265             name: this.frameId,
24266             frameBorder : 'no',
24267             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24268         }, this.el
24269         );
24270         
24271         
24272         this.iframe = iframe.dom;
24273
24274          this.assignDocWin();
24275         
24276         this.doc.designMode = 'on';
24277        
24278         this.doc.open();
24279         this.doc.write(this.getDocMarkup());
24280         this.doc.close();
24281
24282         
24283         var task = { // must defer to wait for browser to be ready
24284             run : function(){
24285                 //console.log("run task?" + this.doc.readyState);
24286                 this.assignDocWin();
24287                 if(this.doc.body || this.doc.readyState == 'complete'){
24288                     try {
24289                         this.doc.designMode="on";
24290                     } catch (e) {
24291                         return;
24292                     }
24293                     Roo.TaskMgr.stop(task);
24294                     this.initEditor.defer(10, this);
24295                 }
24296             },
24297             interval : 10,
24298             duration: 10000,
24299             scope: this
24300         };
24301         Roo.TaskMgr.start(task);
24302
24303     },
24304
24305     // private
24306     onResize : function(w, h)
24307     {
24308          Roo.log('resize: ' +w + ',' + h );
24309         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24310         if(!this.iframe){
24311             return;
24312         }
24313         if(typeof w == 'number'){
24314             
24315             this.iframe.style.width = w + 'px';
24316         }
24317         if(typeof h == 'number'){
24318             
24319             this.iframe.style.height = h + 'px';
24320             if(this.doc){
24321                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24322             }
24323         }
24324         
24325     },
24326
24327     /**
24328      * Toggles the editor between standard and source edit mode.
24329      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24330      */
24331     toggleSourceEdit : function(sourceEditMode){
24332         
24333         this.sourceEditMode = sourceEditMode === true;
24334         
24335         if(this.sourceEditMode){
24336  
24337             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24338             
24339         }else{
24340             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24341             //this.iframe.className = '';
24342             this.deferFocus();
24343         }
24344         //this.setSize(this.owner.wrap.getSize());
24345         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24346     },
24347
24348     
24349   
24350
24351     /**
24352      * Protected method that will not generally be called directly. If you need/want
24353      * custom HTML cleanup, this is the method you should override.
24354      * @param {String} html The HTML to be cleaned
24355      * return {String} The cleaned HTML
24356      */
24357     cleanHtml : function(html){
24358         html = String(html);
24359         if(html.length > 5){
24360             if(Roo.isSafari){ // strip safari nonsense
24361                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24362             }
24363         }
24364         if(html == '&nbsp;'){
24365             html = '';
24366         }
24367         return html;
24368     },
24369
24370     /**
24371      * HTML Editor -> Textarea
24372      * Protected method that will not generally be called directly. Syncs the contents
24373      * of the editor iframe with the textarea.
24374      */
24375     syncValue : function(){
24376         if(this.initialized){
24377             var bd = (this.doc.body || this.doc.documentElement);
24378             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24379             var html = bd.innerHTML;
24380             if(Roo.isSafari){
24381                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24382                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24383                 if(m && m[1]){
24384                     html = '<div style="'+m[0]+'">' + html + '</div>';
24385                 }
24386             }
24387             html = this.cleanHtml(html);
24388             // fix up the special chars.. normaly like back quotes in word...
24389             // however we do not want to do this with chinese..
24390             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24391                 
24392                 var cc = match.charCodeAt();
24393
24394                 // Get the character value, handling surrogate pairs
24395                 if (match.length == 2) {
24396                     // It's a surrogate pair, calculate the Unicode code point
24397                     var high = match.charCodeAt(0) - 0xD800;
24398                     var low  = match.charCodeAt(1) - 0xDC00;
24399                     cc = (high * 0x400) + low + 0x10000;
24400                 }  else if (
24401                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24402                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24403                     (cc >= 0xf900 && cc < 0xfb00 )
24404                 ) {
24405                         return match;
24406                 }  
24407          
24408                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24409                 return "&#" + cc + ";";
24410                 
24411                 
24412             });
24413             
24414             
24415              
24416             if(this.owner.fireEvent('beforesync', this, html) !== false){
24417                 this.el.dom.value = html;
24418                 this.owner.fireEvent('sync', this, html);
24419             }
24420         }
24421     },
24422
24423     /**
24424      * Protected method that will not generally be called directly. Pushes the value of the textarea
24425      * into the iframe editor.
24426      */
24427     pushValue : function(){
24428         if(this.initialized){
24429             var v = this.el.dom.value.trim();
24430             
24431 //            if(v.length < 1){
24432 //                v = '&#160;';
24433 //            }
24434             
24435             if(this.owner.fireEvent('beforepush', this, v) !== false){
24436                 var d = (this.doc.body || this.doc.documentElement);
24437                 d.innerHTML = v;
24438                 this.cleanUpPaste();
24439                 this.el.dom.value = d.innerHTML;
24440                 this.owner.fireEvent('push', this, v);
24441             }
24442         }
24443     },
24444
24445     // private
24446     deferFocus : function(){
24447         this.focus.defer(10, this);
24448     },
24449
24450     // doc'ed in Field
24451     focus : function(){
24452         if(this.win && !this.sourceEditMode){
24453             this.win.focus();
24454         }else{
24455             this.el.focus();
24456         }
24457     },
24458     
24459     assignDocWin: function()
24460     {
24461         var iframe = this.iframe;
24462         
24463          if(Roo.isIE){
24464             this.doc = iframe.contentWindow.document;
24465             this.win = iframe.contentWindow;
24466         } else {
24467 //            if (!Roo.get(this.frameId)) {
24468 //                return;
24469 //            }
24470 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24471 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24472             
24473             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24474                 return;
24475             }
24476             
24477             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24478             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24479         }
24480     },
24481     
24482     // private
24483     initEditor : function(){
24484         //console.log("INIT EDITOR");
24485         this.assignDocWin();
24486         
24487         
24488         
24489         this.doc.designMode="on";
24490         this.doc.open();
24491         this.doc.write(this.getDocMarkup());
24492         this.doc.close();
24493         
24494         var dbody = (this.doc.body || this.doc.documentElement);
24495         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24496         // this copies styles from the containing element into thsi one..
24497         // not sure why we need all of this..
24498         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24499         
24500         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24501         //ss['background-attachment'] = 'fixed'; // w3c
24502         dbody.bgProperties = 'fixed'; // ie
24503         //Roo.DomHelper.applyStyles(dbody, ss);
24504         Roo.EventManager.on(this.doc, {
24505             //'mousedown': this.onEditorEvent,
24506             'mouseup': this.onEditorEvent,
24507             'dblclick': this.onEditorEvent,
24508             'click': this.onEditorEvent,
24509             'keyup': this.onEditorEvent,
24510             buffer:100,
24511             scope: this
24512         });
24513         if(Roo.isGecko){
24514             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24515         }
24516         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24517             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24518         }
24519         this.initialized = true;
24520
24521         this.owner.fireEvent('initialize', this);
24522         this.pushValue();
24523     },
24524
24525     // private
24526     onDestroy : function(){
24527         
24528         
24529         
24530         if(this.rendered){
24531             
24532             //for (var i =0; i < this.toolbars.length;i++) {
24533             //    // fixme - ask toolbars for heights?
24534             //    this.toolbars[i].onDestroy();
24535            // }
24536             
24537             //this.wrap.dom.innerHTML = '';
24538             //this.wrap.remove();
24539         }
24540     },
24541
24542     // private
24543     onFirstFocus : function(){
24544         
24545         this.assignDocWin();
24546         
24547         
24548         this.activated = true;
24549          
24550     
24551         if(Roo.isGecko){ // prevent silly gecko errors
24552             this.win.focus();
24553             var s = this.win.getSelection();
24554             if(!s.focusNode || s.focusNode.nodeType != 3){
24555                 var r = s.getRangeAt(0);
24556                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24557                 r.collapse(true);
24558                 this.deferFocus();
24559             }
24560             try{
24561                 this.execCmd('useCSS', true);
24562                 this.execCmd('styleWithCSS', false);
24563             }catch(e){}
24564         }
24565         this.owner.fireEvent('activate', this);
24566     },
24567
24568     // private
24569     adjustFont: function(btn){
24570         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24571         //if(Roo.isSafari){ // safari
24572         //    adjust *= 2;
24573        // }
24574         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24575         if(Roo.isSafari){ // safari
24576             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24577             v =  (v < 10) ? 10 : v;
24578             v =  (v > 48) ? 48 : v;
24579             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24580             
24581         }
24582         
24583         
24584         v = Math.max(1, v+adjust);
24585         
24586         this.execCmd('FontSize', v  );
24587     },
24588
24589     onEditorEvent : function(e)
24590     {
24591         this.owner.fireEvent('editorevent', this, e);
24592       //  this.updateToolbar();
24593         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24594     },
24595
24596     insertTag : function(tg)
24597     {
24598         // could be a bit smarter... -> wrap the current selected tRoo..
24599         if (tg.toLowerCase() == 'span' ||
24600             tg.toLowerCase() == 'code' ||
24601             tg.toLowerCase() == 'sup' ||
24602             tg.toLowerCase() == 'sub' 
24603             ) {
24604             
24605             range = this.createRange(this.getSelection());
24606             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24607             wrappingNode.appendChild(range.extractContents());
24608             range.insertNode(wrappingNode);
24609
24610             return;
24611             
24612             
24613             
24614         }
24615         this.execCmd("formatblock",   tg);
24616         
24617     },
24618     
24619     insertText : function(txt)
24620     {
24621         
24622         
24623         var range = this.createRange();
24624         range.deleteContents();
24625                //alert(Sender.getAttribute('label'));
24626                
24627         range.insertNode(this.doc.createTextNode(txt));
24628     } ,
24629     
24630      
24631
24632     /**
24633      * Executes a Midas editor command on the editor document and performs necessary focus and
24634      * toolbar updates. <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     relayCmd : function(cmd, value){
24639         this.win.focus();
24640         this.execCmd(cmd, value);
24641         this.owner.fireEvent('editorevent', this);
24642         //this.updateToolbar();
24643         this.owner.deferFocus();
24644     },
24645
24646     /**
24647      * Executes a Midas editor command directly on the editor document.
24648      * For visual commands, you should use {@link #relayCmd} instead.
24649      * <b>This should only be called after the editor is initialized.</b>
24650      * @param {String} cmd The Midas command
24651      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24652      */
24653     execCmd : function(cmd, value){
24654         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24655         this.syncValue();
24656     },
24657  
24658  
24659    
24660     /**
24661      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24662      * to insert tRoo.
24663      * @param {String} text | dom node.. 
24664      */
24665     insertAtCursor : function(text)
24666     {
24667         
24668         if(!this.activated){
24669             return;
24670         }
24671         /*
24672         if(Roo.isIE){
24673             this.win.focus();
24674             var r = this.doc.selection.createRange();
24675             if(r){
24676                 r.collapse(true);
24677                 r.pasteHTML(text);
24678                 this.syncValue();
24679                 this.deferFocus();
24680             
24681             }
24682             return;
24683         }
24684         */
24685         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24686             this.win.focus();
24687             
24688             
24689             // from jquery ui (MIT licenced)
24690             var range, node;
24691             var win = this.win;
24692             
24693             if (win.getSelection && win.getSelection().getRangeAt) {
24694                 range = win.getSelection().getRangeAt(0);
24695                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24696                 range.insertNode(node);
24697             } else if (win.document.selection && win.document.selection.createRange) {
24698                 // no firefox support
24699                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24700                 win.document.selection.createRange().pasteHTML(txt);
24701             } else {
24702                 // no firefox support
24703                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24704                 this.execCmd('InsertHTML', txt);
24705             } 
24706             
24707             this.syncValue();
24708             
24709             this.deferFocus();
24710         }
24711     },
24712  // private
24713     mozKeyPress : function(e){
24714         if(e.ctrlKey){
24715             var c = e.getCharCode(), cmd;
24716           
24717             if(c > 0){
24718                 c = String.fromCharCode(c).toLowerCase();
24719                 switch(c){
24720                     case 'b':
24721                         cmd = 'bold';
24722                         break;
24723                     case 'i':
24724                         cmd = 'italic';
24725                         break;
24726                     
24727                     case 'u':
24728                         cmd = 'underline';
24729                         break;
24730                     
24731                     case 'v':
24732                         this.cleanUpPaste.defer(100, this);
24733                         return;
24734                         
24735                 }
24736                 if(cmd){
24737                     this.win.focus();
24738                     this.execCmd(cmd);
24739                     this.deferFocus();
24740                     e.preventDefault();
24741                 }
24742                 
24743             }
24744         }
24745     },
24746
24747     // private
24748     fixKeys : function(){ // load time branching for fastest keydown performance
24749         if(Roo.isIE){
24750             return function(e){
24751                 var k = e.getKey(), r;
24752                 if(k == e.TAB){
24753                     e.stopEvent();
24754                     r = this.doc.selection.createRange();
24755                     if(r){
24756                         r.collapse(true);
24757                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24758                         this.deferFocus();
24759                     }
24760                     return;
24761                 }
24762                 
24763                 if(k == e.ENTER){
24764                     r = this.doc.selection.createRange();
24765                     if(r){
24766                         var target = r.parentElement();
24767                         if(!target || target.tagName.toLowerCase() != 'li'){
24768                             e.stopEvent();
24769                             r.pasteHTML('<br />');
24770                             r.collapse(false);
24771                             r.select();
24772                         }
24773                     }
24774                 }
24775                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24776                     this.cleanUpPaste.defer(100, this);
24777                     return;
24778                 }
24779                 
24780                 
24781             };
24782         }else if(Roo.isOpera){
24783             return function(e){
24784                 var k = e.getKey();
24785                 if(k == e.TAB){
24786                     e.stopEvent();
24787                     this.win.focus();
24788                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24789                     this.deferFocus();
24790                 }
24791                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24792                     this.cleanUpPaste.defer(100, this);
24793                     return;
24794                 }
24795                 
24796             };
24797         }else if(Roo.isSafari){
24798             return function(e){
24799                 var k = e.getKey();
24800                 
24801                 if(k == e.TAB){
24802                     e.stopEvent();
24803                     this.execCmd('InsertText','\t');
24804                     this.deferFocus();
24805                     return;
24806                 }
24807                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24808                     this.cleanUpPaste.defer(100, this);
24809                     return;
24810                 }
24811                 
24812              };
24813         }
24814     }(),
24815     
24816     getAllAncestors: function()
24817     {
24818         var p = this.getSelectedNode();
24819         var a = [];
24820         if (!p) {
24821             a.push(p); // push blank onto stack..
24822             p = this.getParentElement();
24823         }
24824         
24825         
24826         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24827             a.push(p);
24828             p = p.parentNode;
24829         }
24830         a.push(this.doc.body);
24831         return a;
24832     },
24833     lastSel : false,
24834     lastSelNode : false,
24835     
24836     
24837     getSelection : function() 
24838     {
24839         this.assignDocWin();
24840         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24841     },
24842     
24843     getSelectedNode: function() 
24844     {
24845         // this may only work on Gecko!!!
24846         
24847         // should we cache this!!!!
24848         
24849         
24850         
24851          
24852         var range = this.createRange(this.getSelection()).cloneRange();
24853         
24854         if (Roo.isIE) {
24855             var parent = range.parentElement();
24856             while (true) {
24857                 var testRange = range.duplicate();
24858                 testRange.moveToElementText(parent);
24859                 if (testRange.inRange(range)) {
24860                     break;
24861                 }
24862                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24863                     break;
24864                 }
24865                 parent = parent.parentElement;
24866             }
24867             return parent;
24868         }
24869         
24870         // is ancestor a text element.
24871         var ac =  range.commonAncestorContainer;
24872         if (ac.nodeType == 3) {
24873             ac = ac.parentNode;
24874         }
24875         
24876         var ar = ac.childNodes;
24877          
24878         var nodes = [];
24879         var other_nodes = [];
24880         var has_other_nodes = false;
24881         for (var i=0;i<ar.length;i++) {
24882             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24883                 continue;
24884             }
24885             // fullly contained node.
24886             
24887             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24888                 nodes.push(ar[i]);
24889                 continue;
24890             }
24891             
24892             // probably selected..
24893             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24894                 other_nodes.push(ar[i]);
24895                 continue;
24896             }
24897             // outer..
24898             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24899                 continue;
24900             }
24901             
24902             
24903             has_other_nodes = true;
24904         }
24905         if (!nodes.length && other_nodes.length) {
24906             nodes= other_nodes;
24907         }
24908         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24909             return false;
24910         }
24911         
24912         return nodes[0];
24913     },
24914     createRange: function(sel)
24915     {
24916         // this has strange effects when using with 
24917         // top toolbar - not sure if it's a great idea.
24918         //this.editor.contentWindow.focus();
24919         if (typeof sel != "undefined") {
24920             try {
24921                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24922             } catch(e) {
24923                 return this.doc.createRange();
24924             }
24925         } else {
24926             return this.doc.createRange();
24927         }
24928     },
24929     getParentElement: function()
24930     {
24931         
24932         this.assignDocWin();
24933         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24934         
24935         var range = this.createRange(sel);
24936          
24937         try {
24938             var p = range.commonAncestorContainer;
24939             while (p.nodeType == 3) { // text node
24940                 p = p.parentNode;
24941             }
24942             return p;
24943         } catch (e) {
24944             return null;
24945         }
24946     
24947     },
24948     /***
24949      *
24950      * Range intersection.. the hard stuff...
24951      *  '-1' = before
24952      *  '0' = hits..
24953      *  '1' = after.
24954      *         [ -- selected range --- ]
24955      *   [fail]                        [fail]
24956      *
24957      *    basically..
24958      *      if end is before start or  hits it. fail.
24959      *      if start is after end or hits it fail.
24960      *
24961      *   if either hits (but other is outside. - then it's not 
24962      *   
24963      *    
24964      **/
24965     
24966     
24967     // @see http://www.thismuchiknow.co.uk/?p=64.
24968     rangeIntersectsNode : function(range, node)
24969     {
24970         var nodeRange = node.ownerDocument.createRange();
24971         try {
24972             nodeRange.selectNode(node);
24973         } catch (e) {
24974             nodeRange.selectNodeContents(node);
24975         }
24976     
24977         var rangeStartRange = range.cloneRange();
24978         rangeStartRange.collapse(true);
24979     
24980         var rangeEndRange = range.cloneRange();
24981         rangeEndRange.collapse(false);
24982     
24983         var nodeStartRange = nodeRange.cloneRange();
24984         nodeStartRange.collapse(true);
24985     
24986         var nodeEndRange = nodeRange.cloneRange();
24987         nodeEndRange.collapse(false);
24988     
24989         return rangeStartRange.compareBoundaryPoints(
24990                  Range.START_TO_START, nodeEndRange) == -1 &&
24991                rangeEndRange.compareBoundaryPoints(
24992                  Range.START_TO_START, nodeStartRange) == 1;
24993         
24994          
24995     },
24996     rangeCompareNode : function(range, node)
24997     {
24998         var nodeRange = node.ownerDocument.createRange();
24999         try {
25000             nodeRange.selectNode(node);
25001         } catch (e) {
25002             nodeRange.selectNodeContents(node);
25003         }
25004         
25005         
25006         range.collapse(true);
25007     
25008         nodeRange.collapse(true);
25009      
25010         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25011         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25012          
25013         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25014         
25015         var nodeIsBefore   =  ss == 1;
25016         var nodeIsAfter    = ee == -1;
25017         
25018         if (nodeIsBefore && nodeIsAfter) {
25019             return 0; // outer
25020         }
25021         if (!nodeIsBefore && nodeIsAfter) {
25022             return 1; //right trailed.
25023         }
25024         
25025         if (nodeIsBefore && !nodeIsAfter) {
25026             return 2;  // left trailed.
25027         }
25028         // fully contined.
25029         return 3;
25030     },
25031
25032     // private? - in a new class?
25033     cleanUpPaste :  function()
25034     {
25035         // cleans up the whole document..
25036         Roo.log('cleanuppaste');
25037         
25038         this.cleanUpChildren(this.doc.body);
25039         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25040         if (clean != this.doc.body.innerHTML) {
25041             this.doc.body.innerHTML = clean;
25042         }
25043         
25044     },
25045     
25046     cleanWordChars : function(input) {// change the chars to hex code
25047         var he = Roo.HtmlEditorCore;
25048         
25049         var output = input;
25050         Roo.each(he.swapCodes, function(sw) { 
25051             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25052             
25053             output = output.replace(swapper, sw[1]);
25054         });
25055         
25056         return output;
25057     },
25058     
25059     
25060     cleanUpChildren : function (n)
25061     {
25062         if (!n.childNodes.length) {
25063             return;
25064         }
25065         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25066            this.cleanUpChild(n.childNodes[i]);
25067         }
25068     },
25069     
25070     
25071         
25072     
25073     cleanUpChild : function (node)
25074     {
25075         var ed = this;
25076         //console.log(node);
25077         if (node.nodeName == "#text") {
25078             // clean up silly Windows -- stuff?
25079             return; 
25080         }
25081         if (node.nodeName == "#comment") {
25082             node.parentNode.removeChild(node);
25083             // clean up silly Windows -- stuff?
25084             return; 
25085         }
25086         var lcname = node.tagName.toLowerCase();
25087         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25088         // whitelist of tags..
25089         
25090         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25091             // remove node.
25092             node.parentNode.removeChild(node);
25093             return;
25094             
25095         }
25096         
25097         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25098         
25099         // spans with no attributes - just remove them..
25100         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25101             remove_keep_children = true;
25102         }
25103         
25104         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25105         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25106         
25107         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25108         //    remove_keep_children = true;
25109         //}
25110         
25111         if (remove_keep_children) {
25112             this.cleanUpChildren(node);
25113             // inserts everything just before this node...
25114             while (node.childNodes.length) {
25115                 var cn = node.childNodes[0];
25116                 node.removeChild(cn);
25117                 node.parentNode.insertBefore(cn, node);
25118             }
25119             node.parentNode.removeChild(node);
25120             return;
25121         }
25122         
25123         if (!node.attributes || !node.attributes.length) {
25124             
25125           
25126             
25127             
25128             this.cleanUpChildren(node);
25129             return;
25130         }
25131         
25132         function cleanAttr(n,v)
25133         {
25134             
25135             if (v.match(/^\./) || v.match(/^\//)) {
25136                 return;
25137             }
25138             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25139                 return;
25140             }
25141             if (v.match(/^#/)) {
25142                 return;
25143             }
25144             if (v.match(/^\{/)) { // allow template editing.
25145                 return;
25146             }
25147 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25148             node.removeAttribute(n);
25149             
25150         }
25151         
25152         var cwhite = this.cwhite;
25153         var cblack = this.cblack;
25154             
25155         function cleanStyle(n,v)
25156         {
25157             if (v.match(/expression/)) { //XSS?? should we even bother..
25158                 node.removeAttribute(n);
25159                 return;
25160             }
25161             
25162             var parts = v.split(/;/);
25163             var clean = [];
25164             
25165             Roo.each(parts, function(p) {
25166                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25167                 if (!p.length) {
25168                     return true;
25169                 }
25170                 var l = p.split(':').shift().replace(/\s+/g,'');
25171                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25172                 
25173                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25174 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25175                     //node.removeAttribute(n);
25176                     return true;
25177                 }
25178                 //Roo.log()
25179                 // only allow 'c whitelisted system attributes'
25180                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25181 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25182                     //node.removeAttribute(n);
25183                     return true;
25184                 }
25185                 
25186                 
25187                  
25188                 
25189                 clean.push(p);
25190                 return true;
25191             });
25192             if (clean.length) { 
25193                 node.setAttribute(n, clean.join(';'));
25194             } else {
25195                 node.removeAttribute(n);
25196             }
25197             
25198         }
25199         
25200         
25201         for (var i = node.attributes.length-1; i > -1 ; i--) {
25202             var a = node.attributes[i];
25203             //console.log(a);
25204             
25205             if (a.name.toLowerCase().substr(0,2)=='on')  {
25206                 node.removeAttribute(a.name);
25207                 continue;
25208             }
25209             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25210                 node.removeAttribute(a.name);
25211                 continue;
25212             }
25213             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25214                 cleanAttr(a.name,a.value); // fixme..
25215                 continue;
25216             }
25217             if (a.name == 'style') {
25218                 cleanStyle(a.name,a.value);
25219                 continue;
25220             }
25221             /// clean up MS crap..
25222             // tecnically this should be a list of valid class'es..
25223             
25224             
25225             if (a.name == 'class') {
25226                 if (a.value.match(/^Mso/)) {
25227                     node.removeAttribute('class');
25228                 }
25229                 
25230                 if (a.value.match(/^body$/)) {
25231                     node.removeAttribute('class');
25232                 }
25233                 continue;
25234             }
25235             
25236             // style cleanup!?
25237             // class cleanup?
25238             
25239         }
25240         
25241         
25242         this.cleanUpChildren(node);
25243         
25244         
25245     },
25246     
25247     /**
25248      * Clean up MS wordisms...
25249      */
25250     cleanWord : function(node)
25251     {
25252         if (!node) {
25253             this.cleanWord(this.doc.body);
25254             return;
25255         }
25256         
25257         if(
25258                 node.nodeName == 'SPAN' &&
25259                 !node.hasAttributes() &&
25260                 node.childNodes.length == 1 &&
25261                 node.firstChild.nodeName == "#text"  
25262         ) {
25263             var textNode = node.firstChild;
25264             node.removeChild(textNode);
25265             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25266                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25267             }
25268             node.parentNode.insertBefore(textNode, node);
25269             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25270                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25271             }
25272             node.parentNode.removeChild(node);
25273         }
25274         
25275         if (node.nodeName == "#text") {
25276             // clean up silly Windows -- stuff?
25277             return; 
25278         }
25279         if (node.nodeName == "#comment") {
25280             node.parentNode.removeChild(node);
25281             // clean up silly Windows -- stuff?
25282             return; 
25283         }
25284         
25285         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25286             node.parentNode.removeChild(node);
25287             return;
25288         }
25289         //Roo.log(node.tagName);
25290         // remove - but keep children..
25291         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25292             //Roo.log('-- removed');
25293             while (node.childNodes.length) {
25294                 var cn = node.childNodes[0];
25295                 node.removeChild(cn);
25296                 node.parentNode.insertBefore(cn, node);
25297                 // move node to parent - and clean it..
25298                 this.cleanWord(cn);
25299             }
25300             node.parentNode.removeChild(node);
25301             /// no need to iterate chidlren = it's got none..
25302             //this.iterateChildren(node, this.cleanWord);
25303             return;
25304         }
25305         // clean styles
25306         if (node.className.length) {
25307             
25308             var cn = node.className.split(/\W+/);
25309             var cna = [];
25310             Roo.each(cn, function(cls) {
25311                 if (cls.match(/Mso[a-zA-Z]+/)) {
25312                     return;
25313                 }
25314                 cna.push(cls);
25315             });
25316             node.className = cna.length ? cna.join(' ') : '';
25317             if (!cna.length) {
25318                 node.removeAttribute("class");
25319             }
25320         }
25321         
25322         if (node.hasAttribute("lang")) {
25323             node.removeAttribute("lang");
25324         }
25325         
25326         if (node.hasAttribute("style")) {
25327             
25328             var styles = node.getAttribute("style").split(";");
25329             var nstyle = [];
25330             Roo.each(styles, function(s) {
25331                 if (!s.match(/:/)) {
25332                     return;
25333                 }
25334                 var kv = s.split(":");
25335                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25336                     return;
25337                 }
25338                 // what ever is left... we allow.
25339                 nstyle.push(s);
25340             });
25341             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25342             if (!nstyle.length) {
25343                 node.removeAttribute('style');
25344             }
25345         }
25346         this.iterateChildren(node, this.cleanWord);
25347         
25348         
25349         
25350     },
25351     /**
25352      * iterateChildren of a Node, calling fn each time, using this as the scole..
25353      * @param {DomNode} node node to iterate children of.
25354      * @param {Function} fn method of this class to call on each item.
25355      */
25356     iterateChildren : function(node, fn)
25357     {
25358         if (!node.childNodes.length) {
25359                 return;
25360         }
25361         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25362            fn.call(this, node.childNodes[i])
25363         }
25364     },
25365     
25366     
25367     /**
25368      * cleanTableWidths.
25369      *
25370      * Quite often pasting from word etc.. results in tables with column and widths.
25371      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25372      *
25373      */
25374     cleanTableWidths : function(node)
25375     {
25376          
25377          
25378         if (!node) {
25379             this.cleanTableWidths(this.doc.body);
25380             return;
25381         }
25382         
25383         // ignore list...
25384         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25385             return; 
25386         }
25387         Roo.log(node.tagName);
25388         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25389             this.iterateChildren(node, this.cleanTableWidths);
25390             return;
25391         }
25392         if (node.hasAttribute('width')) {
25393             node.removeAttribute('width');
25394         }
25395         
25396          
25397         if (node.hasAttribute("style")) {
25398             // pretty basic...
25399             
25400             var styles = node.getAttribute("style").split(";");
25401             var nstyle = [];
25402             Roo.each(styles, function(s) {
25403                 if (!s.match(/:/)) {
25404                     return;
25405                 }
25406                 var kv = s.split(":");
25407                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25408                     return;
25409                 }
25410                 // what ever is left... we allow.
25411                 nstyle.push(s);
25412             });
25413             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25414             if (!nstyle.length) {
25415                 node.removeAttribute('style');
25416             }
25417         }
25418         
25419         this.iterateChildren(node, this.cleanTableWidths);
25420         
25421         
25422     },
25423     
25424     
25425     
25426     
25427     domToHTML : function(currentElement, depth, nopadtext) {
25428         
25429         depth = depth || 0;
25430         nopadtext = nopadtext || false;
25431     
25432         if (!currentElement) {
25433             return this.domToHTML(this.doc.body);
25434         }
25435         
25436         //Roo.log(currentElement);
25437         var j;
25438         var allText = false;
25439         var nodeName = currentElement.nodeName;
25440         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25441         
25442         if  (nodeName == '#text') {
25443             
25444             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25445         }
25446         
25447         
25448         var ret = '';
25449         if (nodeName != 'BODY') {
25450              
25451             var i = 0;
25452             // Prints the node tagName, such as <A>, <IMG>, etc
25453             if (tagName) {
25454                 var attr = [];
25455                 for(i = 0; i < currentElement.attributes.length;i++) {
25456                     // quoting?
25457                     var aname = currentElement.attributes.item(i).name;
25458                     if (!currentElement.attributes.item(i).value.length) {
25459                         continue;
25460                     }
25461                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25462                 }
25463                 
25464                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25465             } 
25466             else {
25467                 
25468                 // eack
25469             }
25470         } else {
25471             tagName = false;
25472         }
25473         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25474             return ret;
25475         }
25476         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25477             nopadtext = true;
25478         }
25479         
25480         
25481         // Traverse the tree
25482         i = 0;
25483         var currentElementChild = currentElement.childNodes.item(i);
25484         var allText = true;
25485         var innerHTML  = '';
25486         lastnode = '';
25487         while (currentElementChild) {
25488             // Formatting code (indent the tree so it looks nice on the screen)
25489             var nopad = nopadtext;
25490             if (lastnode == 'SPAN') {
25491                 nopad  = true;
25492             }
25493             // text
25494             if  (currentElementChild.nodeName == '#text') {
25495                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25496                 toadd = nopadtext ? toadd : toadd.trim();
25497                 if (!nopad && toadd.length > 80) {
25498                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25499                 }
25500                 innerHTML  += toadd;
25501                 
25502                 i++;
25503                 currentElementChild = currentElement.childNodes.item(i);
25504                 lastNode = '';
25505                 continue;
25506             }
25507             allText = false;
25508             
25509             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25510                 
25511             // Recursively traverse the tree structure of the child node
25512             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25513             lastnode = currentElementChild.nodeName;
25514             i++;
25515             currentElementChild=currentElement.childNodes.item(i);
25516         }
25517         
25518         ret += innerHTML;
25519         
25520         if (!allText) {
25521                 // The remaining code is mostly for formatting the tree
25522             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25523         }
25524         
25525         
25526         if (tagName) {
25527             ret+= "</"+tagName+">";
25528         }
25529         return ret;
25530         
25531     },
25532         
25533     applyBlacklists : function()
25534     {
25535         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25536         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25537         
25538         this.white = [];
25539         this.black = [];
25540         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25541             if (b.indexOf(tag) > -1) {
25542                 return;
25543             }
25544             this.white.push(tag);
25545             
25546         }, this);
25547         
25548         Roo.each(w, function(tag) {
25549             if (b.indexOf(tag) > -1) {
25550                 return;
25551             }
25552             if (this.white.indexOf(tag) > -1) {
25553                 return;
25554             }
25555             this.white.push(tag);
25556             
25557         }, this);
25558         
25559         
25560         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25561             if (w.indexOf(tag) > -1) {
25562                 return;
25563             }
25564             this.black.push(tag);
25565             
25566         }, this);
25567         
25568         Roo.each(b, function(tag) {
25569             if (w.indexOf(tag) > -1) {
25570                 return;
25571             }
25572             if (this.black.indexOf(tag) > -1) {
25573                 return;
25574             }
25575             this.black.push(tag);
25576             
25577         }, this);
25578         
25579         
25580         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25581         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25582         
25583         this.cwhite = [];
25584         this.cblack = [];
25585         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25586             if (b.indexOf(tag) > -1) {
25587                 return;
25588             }
25589             this.cwhite.push(tag);
25590             
25591         }, this);
25592         
25593         Roo.each(w, function(tag) {
25594             if (b.indexOf(tag) > -1) {
25595                 return;
25596             }
25597             if (this.cwhite.indexOf(tag) > -1) {
25598                 return;
25599             }
25600             this.cwhite.push(tag);
25601             
25602         }, this);
25603         
25604         
25605         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25606             if (w.indexOf(tag) > -1) {
25607                 return;
25608             }
25609             this.cblack.push(tag);
25610             
25611         }, this);
25612         
25613         Roo.each(b, function(tag) {
25614             if (w.indexOf(tag) > -1) {
25615                 return;
25616             }
25617             if (this.cblack.indexOf(tag) > -1) {
25618                 return;
25619             }
25620             this.cblack.push(tag);
25621             
25622         }, this);
25623     },
25624     
25625     setStylesheets : function(stylesheets)
25626     {
25627         if(typeof(stylesheets) == 'string'){
25628             Roo.get(this.iframe.contentDocument.head).createChild({
25629                 tag : 'link',
25630                 rel : 'stylesheet',
25631                 type : 'text/css',
25632                 href : stylesheets
25633             });
25634             
25635             return;
25636         }
25637         var _this = this;
25638      
25639         Roo.each(stylesheets, function(s) {
25640             if(!s.length){
25641                 return;
25642             }
25643             
25644             Roo.get(_this.iframe.contentDocument.head).createChild({
25645                 tag : 'link',
25646                 rel : 'stylesheet',
25647                 type : 'text/css',
25648                 href : s
25649             });
25650         });
25651
25652         
25653     },
25654     
25655     removeStylesheets : function()
25656     {
25657         var _this = this;
25658         
25659         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25660             s.remove();
25661         });
25662     },
25663     
25664     setStyle : function(style)
25665     {
25666         Roo.get(this.iframe.contentDocument.head).createChild({
25667             tag : 'style',
25668             type : 'text/css',
25669             html : style
25670         });
25671
25672         return;
25673     }
25674     
25675     // hide stuff that is not compatible
25676     /**
25677      * @event blur
25678      * @hide
25679      */
25680     /**
25681      * @event change
25682      * @hide
25683      */
25684     /**
25685      * @event focus
25686      * @hide
25687      */
25688     /**
25689      * @event specialkey
25690      * @hide
25691      */
25692     /**
25693      * @cfg {String} fieldClass @hide
25694      */
25695     /**
25696      * @cfg {String} focusClass @hide
25697      */
25698     /**
25699      * @cfg {String} autoCreate @hide
25700      */
25701     /**
25702      * @cfg {String} inputType @hide
25703      */
25704     /**
25705      * @cfg {String} invalidClass @hide
25706      */
25707     /**
25708      * @cfg {String} invalidText @hide
25709      */
25710     /**
25711      * @cfg {String} msgFx @hide
25712      */
25713     /**
25714      * @cfg {String} validateOnBlur @hide
25715      */
25716 });
25717
25718 Roo.HtmlEditorCore.white = [
25719         'area', 'br', 'img', 'input', 'hr', 'wbr',
25720         
25721        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25722        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25723        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25724        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25725        'table',   'ul',         'xmp', 
25726        
25727        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25728       'thead',   'tr', 
25729      
25730       'dir', 'menu', 'ol', 'ul', 'dl',
25731        
25732       'embed',  'object'
25733 ];
25734
25735
25736 Roo.HtmlEditorCore.black = [
25737     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25738         'applet', // 
25739         'base',   'basefont', 'bgsound', 'blink',  'body', 
25740         'frame',  'frameset', 'head',    'html',   'ilayer', 
25741         'iframe', 'layer',  'link',     'meta',    'object',   
25742         'script', 'style' ,'title',  'xml' // clean later..
25743 ];
25744 Roo.HtmlEditorCore.clean = [
25745     'script', 'style', 'title', 'xml'
25746 ];
25747 Roo.HtmlEditorCore.remove = [
25748     'font'
25749 ];
25750 // attributes..
25751
25752 Roo.HtmlEditorCore.ablack = [
25753     'on'
25754 ];
25755     
25756 Roo.HtmlEditorCore.aclean = [ 
25757     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25758 ];
25759
25760 // protocols..
25761 Roo.HtmlEditorCore.pwhite= [
25762         'http',  'https',  'mailto'
25763 ];
25764
25765 // white listed style attributes.
25766 Roo.HtmlEditorCore.cwhite= [
25767       //  'text-align', /// default is to allow most things..
25768       
25769          
25770 //        'font-size'//??
25771 ];
25772
25773 // black listed style attributes.
25774 Roo.HtmlEditorCore.cblack= [
25775       //  'font-size' -- this can be set by the project 
25776 ];
25777
25778
25779 Roo.HtmlEditorCore.swapCodes   =[ 
25780     [    8211, "--" ], 
25781     [    8212, "--" ], 
25782     [    8216,  "'" ],  
25783     [    8217, "'" ],  
25784     [    8220, '"' ],  
25785     [    8221, '"' ],  
25786     [    8226, "*" ],  
25787     [    8230, "..." ]
25788 ]; 
25789
25790     /*
25791  * - LGPL
25792  *
25793  * HtmlEditor
25794  * 
25795  */
25796
25797 /**
25798  * @class Roo.bootstrap.HtmlEditor
25799  * @extends Roo.bootstrap.TextArea
25800  * Bootstrap HtmlEditor class
25801
25802  * @constructor
25803  * Create a new HtmlEditor
25804  * @param {Object} config The config object
25805  */
25806
25807 Roo.bootstrap.HtmlEditor = function(config){
25808     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25809     if (!this.toolbars) {
25810         this.toolbars = [];
25811     }
25812     
25813     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25814     this.addEvents({
25815             /**
25816              * @event initialize
25817              * Fires when the editor is fully initialized (including the iframe)
25818              * @param {HtmlEditor} this
25819              */
25820             initialize: true,
25821             /**
25822              * @event activate
25823              * Fires when the editor is first receives the focus. Any insertion must wait
25824              * until after this event.
25825              * @param {HtmlEditor} this
25826              */
25827             activate: true,
25828              /**
25829              * @event beforesync
25830              * Fires before the textarea is updated with content from the editor iframe. Return false
25831              * to cancel the sync.
25832              * @param {HtmlEditor} this
25833              * @param {String} html
25834              */
25835             beforesync: true,
25836              /**
25837              * @event beforepush
25838              * Fires before the iframe editor is updated with content from the textarea. Return false
25839              * to cancel the push.
25840              * @param {HtmlEditor} this
25841              * @param {String} html
25842              */
25843             beforepush: true,
25844              /**
25845              * @event sync
25846              * Fires when the textarea is updated with content from the editor iframe.
25847              * @param {HtmlEditor} this
25848              * @param {String} html
25849              */
25850             sync: true,
25851              /**
25852              * @event push
25853              * Fires when the iframe editor is updated with content from the textarea.
25854              * @param {HtmlEditor} this
25855              * @param {String} html
25856              */
25857             push: true,
25858              /**
25859              * @event editmodechange
25860              * Fires when the editor switches edit modes
25861              * @param {HtmlEditor} this
25862              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25863              */
25864             editmodechange: true,
25865             /**
25866              * @event editorevent
25867              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25868              * @param {HtmlEditor} this
25869              */
25870             editorevent: true,
25871             /**
25872              * @event firstfocus
25873              * Fires when on first focus - needed by toolbars..
25874              * @param {HtmlEditor} this
25875              */
25876             firstfocus: true,
25877             /**
25878              * @event autosave
25879              * Auto save the htmlEditor value as a file into Events
25880              * @param {HtmlEditor} this
25881              */
25882             autosave: true,
25883             /**
25884              * @event savedpreview
25885              * preview the saved version of htmlEditor
25886              * @param {HtmlEditor} this
25887              */
25888             savedpreview: true
25889         });
25890 };
25891
25892
25893 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25894     
25895     
25896       /**
25897      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25898      */
25899     toolbars : false,
25900     
25901      /**
25902     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25903     */
25904     btns : [],
25905    
25906      /**
25907      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25908      *                        Roo.resizable.
25909      */
25910     resizable : false,
25911      /**
25912      * @cfg {Number} height (in pixels)
25913      */   
25914     height: 300,
25915    /**
25916      * @cfg {Number} width (in pixels)
25917      */   
25918     width: false,
25919     
25920     /**
25921      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25922      * 
25923      */
25924     stylesheets: false,
25925     
25926     // id of frame..
25927     frameId: false,
25928     
25929     // private properties
25930     validationEvent : false,
25931     deferHeight: true,
25932     initialized : false,
25933     activated : false,
25934     
25935     onFocus : Roo.emptyFn,
25936     iframePad:3,
25937     hideMode:'offsets',
25938     
25939     tbContainer : false,
25940     
25941     bodyCls : '',
25942     
25943     toolbarContainer :function() {
25944         return this.wrap.select('.x-html-editor-tb',true).first();
25945     },
25946
25947     /**
25948      * Protected method that will not generally be called directly. It
25949      * is called when the editor creates its toolbar. Override this method if you need to
25950      * add custom toolbar buttons.
25951      * @param {HtmlEditor} editor
25952      */
25953     createToolbar : function(){
25954         Roo.log('renewing');
25955         Roo.log("create toolbars");
25956         
25957         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25958         this.toolbars[0].render(this.toolbarContainer());
25959         
25960         return;
25961         
25962 //        if (!editor.toolbars || !editor.toolbars.length) {
25963 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25964 //        }
25965 //        
25966 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25967 //            editor.toolbars[i] = Roo.factory(
25968 //                    typeof(editor.toolbars[i]) == 'string' ?
25969 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25970 //                Roo.bootstrap.HtmlEditor);
25971 //            editor.toolbars[i].init(editor);
25972 //        }
25973     },
25974
25975      
25976     // private
25977     onRender : function(ct, position)
25978     {
25979        // Roo.log("Call onRender: " + this.xtype);
25980         var _t = this;
25981         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25982       
25983         this.wrap = this.inputEl().wrap({
25984             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25985         });
25986         
25987         this.editorcore.onRender(ct, position);
25988          
25989         if (this.resizable) {
25990             this.resizeEl = new Roo.Resizable(this.wrap, {
25991                 pinned : true,
25992                 wrap: true,
25993                 dynamic : true,
25994                 minHeight : this.height,
25995                 height: this.height,
25996                 handles : this.resizable,
25997                 width: this.width,
25998                 listeners : {
25999                     resize : function(r, w, h) {
26000                         _t.onResize(w,h); // -something
26001                     }
26002                 }
26003             });
26004             
26005         }
26006         this.createToolbar(this);
26007        
26008         
26009         if(!this.width && this.resizable){
26010             this.setSize(this.wrap.getSize());
26011         }
26012         if (this.resizeEl) {
26013             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26014             // should trigger onReize..
26015         }
26016         
26017     },
26018
26019     // private
26020     onResize : function(w, h)
26021     {
26022         Roo.log('resize: ' +w + ',' + h );
26023         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26024         var ew = false;
26025         var eh = false;
26026         
26027         if(this.inputEl() ){
26028             if(typeof w == 'number'){
26029                 var aw = w - this.wrap.getFrameWidth('lr');
26030                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26031                 ew = aw;
26032             }
26033             if(typeof h == 'number'){
26034                  var tbh = -11;  // fixme it needs to tool bar size!
26035                 for (var i =0; i < this.toolbars.length;i++) {
26036                     // fixme - ask toolbars for heights?
26037                     tbh += this.toolbars[i].el.getHeight();
26038                     //if (this.toolbars[i].footer) {
26039                     //    tbh += this.toolbars[i].footer.el.getHeight();
26040                     //}
26041                 }
26042               
26043                 
26044                 
26045                 
26046                 
26047                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26048                 ah -= 5; // knock a few pixes off for look..
26049                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26050                 var eh = ah;
26051             }
26052         }
26053         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26054         this.editorcore.onResize(ew,eh);
26055         
26056     },
26057
26058     /**
26059      * Toggles the editor between standard and source edit mode.
26060      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26061      */
26062     toggleSourceEdit : function(sourceEditMode)
26063     {
26064         this.editorcore.toggleSourceEdit(sourceEditMode);
26065         
26066         if(this.editorcore.sourceEditMode){
26067             Roo.log('editor - showing textarea');
26068             
26069 //            Roo.log('in');
26070 //            Roo.log(this.syncValue());
26071             this.syncValue();
26072             this.inputEl().removeClass(['hide', 'x-hidden']);
26073             this.inputEl().dom.removeAttribute('tabIndex');
26074             this.inputEl().focus();
26075         }else{
26076             Roo.log('editor - hiding textarea');
26077 //            Roo.log('out')
26078 //            Roo.log(this.pushValue()); 
26079             this.pushValue();
26080             
26081             this.inputEl().addClass(['hide', 'x-hidden']);
26082             this.inputEl().dom.setAttribute('tabIndex', -1);
26083             //this.deferFocus();
26084         }
26085          
26086         if(this.resizable){
26087             this.setSize(this.wrap.getSize());
26088         }
26089         
26090         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26091     },
26092  
26093     // private (for BoxComponent)
26094     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26095
26096     // private (for BoxComponent)
26097     getResizeEl : function(){
26098         return this.wrap;
26099     },
26100
26101     // private (for BoxComponent)
26102     getPositionEl : function(){
26103         return this.wrap;
26104     },
26105
26106     // private
26107     initEvents : function(){
26108         this.originalValue = this.getValue();
26109     },
26110
26111 //    /**
26112 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26113 //     * @method
26114 //     */
26115 //    markInvalid : Roo.emptyFn,
26116 //    /**
26117 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26118 //     * @method
26119 //     */
26120 //    clearInvalid : Roo.emptyFn,
26121
26122     setValue : function(v){
26123         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26124         this.editorcore.pushValue();
26125     },
26126
26127      
26128     // private
26129     deferFocus : function(){
26130         this.focus.defer(10, this);
26131     },
26132
26133     // doc'ed in Field
26134     focus : function(){
26135         this.editorcore.focus();
26136         
26137     },
26138       
26139
26140     // private
26141     onDestroy : function(){
26142         
26143         
26144         
26145         if(this.rendered){
26146             
26147             for (var i =0; i < this.toolbars.length;i++) {
26148                 // fixme - ask toolbars for heights?
26149                 this.toolbars[i].onDestroy();
26150             }
26151             
26152             this.wrap.dom.innerHTML = '';
26153             this.wrap.remove();
26154         }
26155     },
26156
26157     // private
26158     onFirstFocus : function(){
26159         //Roo.log("onFirstFocus");
26160         this.editorcore.onFirstFocus();
26161          for (var i =0; i < this.toolbars.length;i++) {
26162             this.toolbars[i].onFirstFocus();
26163         }
26164         
26165     },
26166     
26167     // private
26168     syncValue : function()
26169     {   
26170         this.editorcore.syncValue();
26171     },
26172     
26173     pushValue : function()
26174     {   
26175         this.editorcore.pushValue();
26176     }
26177      
26178     
26179     // hide stuff that is not compatible
26180     /**
26181      * @event blur
26182      * @hide
26183      */
26184     /**
26185      * @event change
26186      * @hide
26187      */
26188     /**
26189      * @event focus
26190      * @hide
26191      */
26192     /**
26193      * @event specialkey
26194      * @hide
26195      */
26196     /**
26197      * @cfg {String} fieldClass @hide
26198      */
26199     /**
26200      * @cfg {String} focusClass @hide
26201      */
26202     /**
26203      * @cfg {String} autoCreate @hide
26204      */
26205     /**
26206      * @cfg {String} inputType @hide
26207      */
26208      
26209     /**
26210      * @cfg {String} invalidText @hide
26211      */
26212     /**
26213      * @cfg {String} msgFx @hide
26214      */
26215     /**
26216      * @cfg {String} validateOnBlur @hide
26217      */
26218 });
26219  
26220     
26221    
26222    
26223    
26224       
26225 Roo.namespace('Roo.bootstrap.htmleditor');
26226 /**
26227  * @class Roo.bootstrap.HtmlEditorToolbar1
26228  * Basic Toolbar
26229  * 
26230  * @example
26231  * Usage:
26232  *
26233  new Roo.bootstrap.HtmlEditor({
26234     ....
26235     toolbars : [
26236         new Roo.bootstrap.HtmlEditorToolbar1({
26237             disable : { fonts: 1 , format: 1, ..., ... , ...],
26238             btns : [ .... ]
26239         })
26240     }
26241      
26242  * 
26243  * @cfg {Object} disable List of elements to disable..
26244  * @cfg {Array} btns List of additional buttons.
26245  * 
26246  * 
26247  * NEEDS Extra CSS? 
26248  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26249  */
26250  
26251 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26252 {
26253     
26254     Roo.apply(this, config);
26255     
26256     // default disabled, based on 'good practice'..
26257     this.disable = this.disable || {};
26258     Roo.applyIf(this.disable, {
26259         fontSize : true,
26260         colors : true,
26261         specialElements : true
26262     });
26263     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26264     
26265     this.editor = config.editor;
26266     this.editorcore = config.editor.editorcore;
26267     
26268     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26269     
26270     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26271     // dont call parent... till later.
26272 }
26273 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26274      
26275     bar : true,
26276     
26277     editor : false,
26278     editorcore : false,
26279     
26280     
26281     formats : [
26282         "p" ,  
26283         "h1","h2","h3","h4","h5","h6", 
26284         "pre", "code", 
26285         "abbr", "acronym", "address", "cite", "samp", "var",
26286         'div','span'
26287     ],
26288     
26289     onRender : function(ct, position)
26290     {
26291        // Roo.log("Call onRender: " + this.xtype);
26292         
26293        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26294        Roo.log(this.el);
26295        this.el.dom.style.marginBottom = '0';
26296        var _this = this;
26297        var editorcore = this.editorcore;
26298        var editor= this.editor;
26299        
26300        var children = [];
26301        var btn = function(id,cmd , toggle, handler, html){
26302        
26303             var  event = toggle ? 'toggle' : 'click';
26304        
26305             var a = {
26306                 size : 'sm',
26307                 xtype: 'Button',
26308                 xns: Roo.bootstrap,
26309                 //glyphicon : id,
26310                 fa: id,
26311                 cmd : id || cmd,
26312                 enableToggle:toggle !== false,
26313                 html : html || '',
26314                 pressed : toggle ? false : null,
26315                 listeners : {}
26316             };
26317             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26318                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26319             };
26320             children.push(a);
26321             return a;
26322        }
26323        
26324     //    var cb_box = function...
26325         
26326         var style = {
26327                 xtype: 'Button',
26328                 size : 'sm',
26329                 xns: Roo.bootstrap,
26330                 fa : 'font',
26331                 //html : 'submit'
26332                 menu : {
26333                     xtype: 'Menu',
26334                     xns: Roo.bootstrap,
26335                     items:  []
26336                 }
26337         };
26338         Roo.each(this.formats, function(f) {
26339             style.menu.items.push({
26340                 xtype :'MenuItem',
26341                 xns: Roo.bootstrap,
26342                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26343                 tagname : f,
26344                 listeners : {
26345                     click : function()
26346                     {
26347                         editorcore.insertTag(this.tagname);
26348                         editor.focus();
26349                     }
26350                 }
26351                 
26352             });
26353         });
26354         children.push(style);   
26355         
26356         btn('bold',false,true);
26357         btn('italic',false,true);
26358         btn('align-left', 'justifyleft',true);
26359         btn('align-center', 'justifycenter',true);
26360         btn('align-right' , 'justifyright',true);
26361         btn('link', false, false, function(btn) {
26362             //Roo.log("create link?");
26363             var url = prompt(this.createLinkText, this.defaultLinkValue);
26364             if(url && url != 'http:/'+'/'){
26365                 this.editorcore.relayCmd('createlink', url);
26366             }
26367         }),
26368         btn('list','insertunorderedlist',true);
26369         btn('pencil', false,true, function(btn){
26370                 Roo.log(this);
26371                 this.toggleSourceEdit(btn.pressed);
26372         });
26373         
26374         if (this.editor.btns.length > 0) {
26375             for (var i = 0; i<this.editor.btns.length; i++) {
26376                 children.push(this.editor.btns[i]);
26377             }
26378         }
26379         
26380         /*
26381         var cog = {
26382                 xtype: 'Button',
26383                 size : 'sm',
26384                 xns: Roo.bootstrap,
26385                 glyphicon : 'cog',
26386                 //html : 'submit'
26387                 menu : {
26388                     xtype: 'Menu',
26389                     xns: Roo.bootstrap,
26390                     items:  []
26391                 }
26392         };
26393         
26394         cog.menu.items.push({
26395             xtype :'MenuItem',
26396             xns: Roo.bootstrap,
26397             html : Clean styles,
26398             tagname : f,
26399             listeners : {
26400                 click : function()
26401                 {
26402                     editorcore.insertTag(this.tagname);
26403                     editor.focus();
26404                 }
26405             }
26406             
26407         });
26408        */
26409         
26410          
26411        this.xtype = 'NavSimplebar';
26412         
26413         for(var i=0;i< children.length;i++) {
26414             
26415             this.buttons.add(this.addxtypeChild(children[i]));
26416             
26417         }
26418         
26419         editor.on('editorevent', this.updateToolbar, this);
26420     },
26421     onBtnClick : function(id)
26422     {
26423        this.editorcore.relayCmd(id);
26424        this.editorcore.focus();
26425     },
26426     
26427     /**
26428      * Protected method that will not generally be called directly. It triggers
26429      * a toolbar update by reading the markup state of the current selection in the editor.
26430      */
26431     updateToolbar: function(){
26432
26433         if(!this.editorcore.activated){
26434             this.editor.onFirstFocus(); // is this neeed?
26435             return;
26436         }
26437
26438         var btns = this.buttons; 
26439         var doc = this.editorcore.doc;
26440         btns.get('bold').setActive(doc.queryCommandState('bold'));
26441         btns.get('italic').setActive(doc.queryCommandState('italic'));
26442         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26443         
26444         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26445         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26446         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26447         
26448         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26449         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26450          /*
26451         
26452         var ans = this.editorcore.getAllAncestors();
26453         if (this.formatCombo) {
26454             
26455             
26456             var store = this.formatCombo.store;
26457             this.formatCombo.setValue("");
26458             for (var i =0; i < ans.length;i++) {
26459                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26460                     // select it..
26461                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26462                     break;
26463                 }
26464             }
26465         }
26466         
26467         
26468         
26469         // hides menus... - so this cant be on a menu...
26470         Roo.bootstrap.MenuMgr.hideAll();
26471         */
26472         Roo.bootstrap.MenuMgr.hideAll();
26473         //this.editorsyncValue();
26474     },
26475     onFirstFocus: function() {
26476         this.buttons.each(function(item){
26477            item.enable();
26478         });
26479     },
26480     toggleSourceEdit : function(sourceEditMode){
26481         
26482           
26483         if(sourceEditMode){
26484             Roo.log("disabling buttons");
26485            this.buttons.each( function(item){
26486                 if(item.cmd != 'pencil'){
26487                     item.disable();
26488                 }
26489             });
26490           
26491         }else{
26492             Roo.log("enabling buttons");
26493             if(this.editorcore.initialized){
26494                 this.buttons.each( function(item){
26495                     item.enable();
26496                 });
26497             }
26498             
26499         }
26500         Roo.log("calling toggole on editor");
26501         // tell the editor that it's been pressed..
26502         this.editor.toggleSourceEdit(sourceEditMode);
26503        
26504     }
26505 });
26506
26507
26508
26509
26510  
26511 /*
26512  * - LGPL
26513  */
26514
26515 /**
26516  * @class Roo.bootstrap.Markdown
26517  * @extends Roo.bootstrap.TextArea
26518  * Bootstrap Showdown editable area
26519  * @cfg {string} content
26520  * 
26521  * @constructor
26522  * Create a new Showdown
26523  */
26524
26525 Roo.bootstrap.Markdown = function(config){
26526     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26527    
26528 };
26529
26530 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26531     
26532     editing :false,
26533     
26534     initEvents : function()
26535     {
26536         
26537         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26538         this.markdownEl = this.el.createChild({
26539             cls : 'roo-markdown-area'
26540         });
26541         this.inputEl().addClass('d-none');
26542         if (this.getValue() == '') {
26543             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26544             
26545         } else {
26546             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26547         }
26548         this.markdownEl.on('click', this.toggleTextEdit, this);
26549         this.on('blur', this.toggleTextEdit, this);
26550         this.on('specialkey', this.resizeTextArea, this);
26551     },
26552     
26553     toggleTextEdit : function()
26554     {
26555         var sh = this.markdownEl.getHeight();
26556         this.inputEl().addClass('d-none');
26557         this.markdownEl.addClass('d-none');
26558         if (!this.editing) {
26559             // show editor?
26560             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26561             this.inputEl().removeClass('d-none');
26562             this.inputEl().focus();
26563             this.editing = true;
26564             return;
26565         }
26566         // show showdown...
26567         this.updateMarkdown();
26568         this.markdownEl.removeClass('d-none');
26569         this.editing = false;
26570         return;
26571     },
26572     updateMarkdown : function()
26573     {
26574         if (this.getValue() == '') {
26575             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26576             return;
26577         }
26578  
26579         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26580     },
26581     
26582     resizeTextArea: function () {
26583         
26584         var sh = 100;
26585         Roo.log([sh, this.getValue().split("\n").length * 30]);
26586         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26587     },
26588     setValue : function(val)
26589     {
26590         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26591         if (!this.editing) {
26592             this.updateMarkdown();
26593         }
26594         
26595     },
26596     focus : function()
26597     {
26598         if (!this.editing) {
26599             this.toggleTextEdit();
26600         }
26601         
26602     }
26603
26604
26605 });
26606 /**
26607  * @class Roo.bootstrap.Table.AbstractSelectionModel
26608  * @extends Roo.util.Observable
26609  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26610  * implemented by descendant classes.  This class should not be directly instantiated.
26611  * @constructor
26612  */
26613 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26614     this.locked = false;
26615     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26616 };
26617
26618
26619 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26620     /** @ignore Called by the grid automatically. Do not call directly. */
26621     init : function(grid){
26622         this.grid = grid;
26623         this.initEvents();
26624     },
26625
26626     /**
26627      * Locks the selections.
26628      */
26629     lock : function(){
26630         this.locked = true;
26631     },
26632
26633     /**
26634      * Unlocks the selections.
26635      */
26636     unlock : function(){
26637         this.locked = false;
26638     },
26639
26640     /**
26641      * Returns true if the selections are locked.
26642      * @return {Boolean}
26643      */
26644     isLocked : function(){
26645         return this.locked;
26646     },
26647     
26648     
26649     initEvents : function ()
26650     {
26651         
26652     }
26653 });
26654 /**
26655  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26656  * @class Roo.bootstrap.Table.RowSelectionModel
26657  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26658  * It supports multiple selections and keyboard selection/navigation. 
26659  * @constructor
26660  * @param {Object} config
26661  */
26662
26663 Roo.bootstrap.Table.RowSelectionModel = function(config){
26664     Roo.apply(this, config);
26665     this.selections = new Roo.util.MixedCollection(false, function(o){
26666         return o.id;
26667     });
26668
26669     this.last = false;
26670     this.lastActive = false;
26671
26672     this.addEvents({
26673         /**
26674              * @event selectionchange
26675              * Fires when the selection changes
26676              * @param {SelectionModel} this
26677              */
26678             "selectionchange" : true,
26679         /**
26680              * @event afterselectionchange
26681              * Fires after the selection changes (eg. by key press or clicking)
26682              * @param {SelectionModel} this
26683              */
26684             "afterselectionchange" : true,
26685         /**
26686              * @event beforerowselect
26687              * Fires when a row is selected being selected, return false to cancel.
26688              * @param {SelectionModel} this
26689              * @param {Number} rowIndex The selected index
26690              * @param {Boolean} keepExisting False if other selections will be cleared
26691              */
26692             "beforerowselect" : true,
26693         /**
26694              * @event rowselect
26695              * Fires when a row is selected.
26696              * @param {SelectionModel} this
26697              * @param {Number} rowIndex The selected index
26698              * @param {Roo.data.Record} r The record
26699              */
26700             "rowselect" : true,
26701         /**
26702              * @event rowdeselect
26703              * Fires when a row is deselected.
26704              * @param {SelectionModel} this
26705              * @param {Number} rowIndex The selected index
26706              */
26707         "rowdeselect" : true
26708     });
26709     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26710     this.locked = false;
26711  };
26712
26713 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26714     /**
26715      * @cfg {Boolean} singleSelect
26716      * True to allow selection of only one row at a time (defaults to false)
26717      */
26718     singleSelect : false,
26719
26720     // private
26721     initEvents : function()
26722     {
26723
26724         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26725         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26726         //}else{ // allow click to work like normal
26727          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26728         //}
26729         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26730         this.grid.on("rowclick", this.handleMouseDown, this);
26731         
26732         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26733             "up" : function(e){
26734                 if(!e.shiftKey){
26735                     this.selectPrevious(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             "down" : function(e){
26749                 if(!e.shiftKey){
26750                     this.selectNext(e.shiftKey);
26751                 }else if(this.last !== false && this.lastActive !== false){
26752                     var last = this.last;
26753                     this.selectRange(this.last,  this.lastActive+1);
26754                     this.grid.getView().focusRow(this.lastActive);
26755                     if(last !== false){
26756                         this.last = last;
26757                     }
26758                 }else{
26759                     this.selectFirstRow();
26760                 }
26761                 this.fireEvent("afterselectionchange", this);
26762             },
26763             scope: this
26764         });
26765         this.grid.store.on('load', function(){
26766             this.selections.clear();
26767         },this);
26768         /*
26769         var view = this.grid.view;
26770         view.on("refresh", this.onRefresh, this);
26771         view.on("rowupdated", this.onRowUpdated, this);
26772         view.on("rowremoved", this.onRemove, this);
26773         */
26774     },
26775
26776     // private
26777     onRefresh : function()
26778     {
26779         var ds = this.grid.store, i, v = this.grid.view;
26780         var s = this.selections;
26781         s.each(function(r){
26782             if((i = ds.indexOfId(r.id)) != -1){
26783                 v.onRowSelect(i);
26784             }else{
26785                 s.remove(r);
26786             }
26787         });
26788     },
26789
26790     // private
26791     onRemove : function(v, index, r){
26792         this.selections.remove(r);
26793     },
26794
26795     // private
26796     onRowUpdated : function(v, index, r){
26797         if(this.isSelected(r)){
26798             v.onRowSelect(index);
26799         }
26800     },
26801
26802     /**
26803      * Select records.
26804      * @param {Array} records The records to select
26805      * @param {Boolean} keepExisting (optional) True to keep existing selections
26806      */
26807     selectRecords : function(records, keepExisting)
26808     {
26809         if(!keepExisting){
26810             this.clearSelections();
26811         }
26812             var ds = this.grid.store;
26813         for(var i = 0, len = records.length; i < len; i++){
26814             this.selectRow(ds.indexOf(records[i]), true);
26815         }
26816     },
26817
26818     /**
26819      * Gets the number of selected rows.
26820      * @return {Number}
26821      */
26822     getCount : function(){
26823         return this.selections.length;
26824     },
26825
26826     /**
26827      * Selects the first row in the grid.
26828      */
26829     selectFirstRow : function(){
26830         this.selectRow(0);
26831     },
26832
26833     /**
26834      * Select the last row.
26835      * @param {Boolean} keepExisting (optional) True to keep existing selections
26836      */
26837     selectLastRow : function(keepExisting){
26838         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26839         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26840     },
26841
26842     /**
26843      * Selects the row immediately following the last selected row.
26844      * @param {Boolean} keepExisting (optional) True to keep existing selections
26845      */
26846     selectNext : function(keepExisting)
26847     {
26848             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26849             this.selectRow(this.last+1, keepExisting);
26850             this.grid.getView().focusRow(this.last);
26851         }
26852     },
26853
26854     /**
26855      * Selects the row that precedes the last selected row.
26856      * @param {Boolean} keepExisting (optional) True to keep existing selections
26857      */
26858     selectPrevious : function(keepExisting){
26859         if(this.last){
26860             this.selectRow(this.last-1, keepExisting);
26861             this.grid.getView().focusRow(this.last);
26862         }
26863     },
26864
26865     /**
26866      * Returns the selected records
26867      * @return {Array} Array of selected records
26868      */
26869     getSelections : function(){
26870         return [].concat(this.selections.items);
26871     },
26872
26873     /**
26874      * Returns the first selected record.
26875      * @return {Record}
26876      */
26877     getSelected : function(){
26878         return this.selections.itemAt(0);
26879     },
26880
26881
26882     /**
26883      * Clears all selections.
26884      */
26885     clearSelections : function(fast)
26886     {
26887         if(this.locked) {
26888             return;
26889         }
26890         if(fast !== true){
26891                 var ds = this.grid.store;
26892             var s = this.selections;
26893             s.each(function(r){
26894                 this.deselectRow(ds.indexOfId(r.id));
26895             }, this);
26896             s.clear();
26897         }else{
26898             this.selections.clear();
26899         }
26900         this.last = false;
26901     },
26902
26903
26904     /**
26905      * Selects all rows.
26906      */
26907     selectAll : function(){
26908         if(this.locked) {
26909             return;
26910         }
26911         this.selections.clear();
26912         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26913             this.selectRow(i, true);
26914         }
26915     },
26916
26917     /**
26918      * Returns True if there is a selection.
26919      * @return {Boolean}
26920      */
26921     hasSelection : function(){
26922         return this.selections.length > 0;
26923     },
26924
26925     /**
26926      * Returns True if the specified row is selected.
26927      * @param {Number/Record} record The record or index of the record to check
26928      * @return {Boolean}
26929      */
26930     isSelected : function(index){
26931             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26932         return (r && this.selections.key(r.id) ? true : false);
26933     },
26934
26935     /**
26936      * Returns True if the specified record id is selected.
26937      * @param {String} id The id of record to check
26938      * @return {Boolean}
26939      */
26940     isIdSelected : function(id){
26941         return (this.selections.key(id) ? true : false);
26942     },
26943
26944
26945     // private
26946     handleMouseDBClick : function(e, t){
26947         
26948     },
26949     // private
26950     handleMouseDown : function(e, t)
26951     {
26952             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26953         if(this.isLocked() || rowIndex < 0 ){
26954             return;
26955         };
26956         if(e.shiftKey && this.last !== false){
26957             var last = this.last;
26958             this.selectRange(last, rowIndex, e.ctrlKey);
26959             this.last = last; // reset the last
26960             t.focus();
26961     
26962         }else{
26963             var isSelected = this.isSelected(rowIndex);
26964             //Roo.log("select row:" + rowIndex);
26965             if(isSelected){
26966                 this.deselectRow(rowIndex);
26967             } else {
26968                         this.selectRow(rowIndex, true);
26969             }
26970     
26971             /*
26972                 if(e.button !== 0 && isSelected){
26973                 alert('rowIndex 2: ' + rowIndex);
26974                     view.focusRow(rowIndex);
26975                 }else if(e.ctrlKey && isSelected){
26976                     this.deselectRow(rowIndex);
26977                 }else if(!isSelected){
26978                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26979                     view.focusRow(rowIndex);
26980                 }
26981             */
26982         }
26983         this.fireEvent("afterselectionchange", this);
26984     },
26985     // private
26986     handleDragableRowClick :  function(grid, rowIndex, e) 
26987     {
26988         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26989             this.selectRow(rowIndex, false);
26990             grid.view.focusRow(rowIndex);
26991              this.fireEvent("afterselectionchange", this);
26992         }
26993     },
26994     
26995     /**
26996      * Selects multiple rows.
26997      * @param {Array} rows Array of the indexes of the row to select
26998      * @param {Boolean} keepExisting (optional) True to keep existing selections
26999      */
27000     selectRows : function(rows, keepExisting){
27001         if(!keepExisting){
27002             this.clearSelections();
27003         }
27004         for(var i = 0, len = rows.length; i < len; i++){
27005             this.selectRow(rows[i], true);
27006         }
27007     },
27008
27009     /**
27010      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27011      * @param {Number} startRow The index of the first row in the range
27012      * @param {Number} endRow The index of the last row in the range
27013      * @param {Boolean} keepExisting (optional) True to retain existing selections
27014      */
27015     selectRange : function(startRow, endRow, keepExisting){
27016         if(this.locked) {
27017             return;
27018         }
27019         if(!keepExisting){
27020             this.clearSelections();
27021         }
27022         if(startRow <= endRow){
27023             for(var i = startRow; i <= endRow; i++){
27024                 this.selectRow(i, true);
27025             }
27026         }else{
27027             for(var i = startRow; i >= endRow; i--){
27028                 this.selectRow(i, true);
27029             }
27030         }
27031     },
27032
27033     /**
27034      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27035      * @param {Number} startRow The index of the first row in the range
27036      * @param {Number} endRow The index of the last row in the range
27037      */
27038     deselectRange : function(startRow, endRow, preventViewNotify){
27039         if(this.locked) {
27040             return;
27041         }
27042         for(var i = startRow; i <= endRow; i++){
27043             this.deselectRow(i, preventViewNotify);
27044         }
27045     },
27046
27047     /**
27048      * Selects a row.
27049      * @param {Number} row The index of the row to select
27050      * @param {Boolean} keepExisting (optional) True to keep existing selections
27051      */
27052     selectRow : function(index, keepExisting, preventViewNotify)
27053     {
27054             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27055             return;
27056         }
27057         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27058             if(!keepExisting || this.singleSelect){
27059                 this.clearSelections();
27060             }
27061             
27062             var r = this.grid.store.getAt(index);
27063             //console.log('selectRow - record id :' + r.id);
27064             
27065             this.selections.add(r);
27066             this.last = this.lastActive = index;
27067             if(!preventViewNotify){
27068                 var proxy = new Roo.Element(
27069                                 this.grid.getRowDom(index)
27070                 );
27071                 proxy.addClass('bg-info info');
27072             }
27073             this.fireEvent("rowselect", this, index, r);
27074             this.fireEvent("selectionchange", this);
27075         }
27076     },
27077
27078     /**
27079      * Deselects a row.
27080      * @param {Number} row The index of the row to deselect
27081      */
27082     deselectRow : function(index, preventViewNotify)
27083     {
27084         if(this.locked) {
27085             return;
27086         }
27087         if(this.last == index){
27088             this.last = false;
27089         }
27090         if(this.lastActive == index){
27091             this.lastActive = false;
27092         }
27093         
27094         var r = this.grid.store.getAt(index);
27095         if (!r) {
27096             return;
27097         }
27098         
27099         this.selections.remove(r);
27100         //.console.log('deselectRow - record id :' + r.id);
27101         if(!preventViewNotify){
27102         
27103             var proxy = new Roo.Element(
27104                 this.grid.getRowDom(index)
27105             );
27106             proxy.removeClass('bg-info info');
27107         }
27108         this.fireEvent("rowdeselect", this, index);
27109         this.fireEvent("selectionchange", this);
27110     },
27111
27112     // private
27113     restoreLast : function(){
27114         if(this._last){
27115             this.last = this._last;
27116         }
27117     },
27118
27119     // private
27120     acceptsNav : function(row, col, cm){
27121         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27122     },
27123
27124     // private
27125     onEditorKey : function(field, e){
27126         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27127         if(k == e.TAB){
27128             e.stopEvent();
27129             ed.completeEdit();
27130             if(e.shiftKey){
27131                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27132             }else{
27133                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27134             }
27135         }else if(k == e.ENTER && !e.ctrlKey){
27136             e.stopEvent();
27137             ed.completeEdit();
27138             if(e.shiftKey){
27139                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27140             }else{
27141                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27142             }
27143         }else if(k == e.ESC){
27144             ed.cancelEdit();
27145         }
27146         if(newCell){
27147             g.startEditing(newCell[0], newCell[1]);
27148         }
27149     }
27150 });
27151 /*
27152  * Based on:
27153  * Ext JS Library 1.1.1
27154  * Copyright(c) 2006-2007, Ext JS, LLC.
27155  *
27156  * Originally Released Under LGPL - original licence link has changed is not relivant.
27157  *
27158  * Fork - LGPL
27159  * <script type="text/javascript">
27160  */
27161  
27162 /**
27163  * @class Roo.bootstrap.PagingToolbar
27164  * @extends Roo.bootstrap.NavSimplebar
27165  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27166  * @constructor
27167  * Create a new PagingToolbar
27168  * @param {Object} config The config object
27169  * @param {Roo.data.Store} store
27170  */
27171 Roo.bootstrap.PagingToolbar = function(config)
27172 {
27173     // old args format still supported... - xtype is prefered..
27174         // created from xtype...
27175     
27176     this.ds = config.dataSource;
27177     
27178     if (config.store && !this.ds) {
27179         this.store= Roo.factory(config.store, Roo.data);
27180         this.ds = this.store;
27181         this.ds.xmodule = this.xmodule || false;
27182     }
27183     
27184     this.toolbarItems = [];
27185     if (config.items) {
27186         this.toolbarItems = config.items;
27187     }
27188     
27189     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27190     
27191     this.cursor = 0;
27192     
27193     if (this.ds) { 
27194         this.bind(this.ds);
27195     }
27196     
27197     if (Roo.bootstrap.version == 4) {
27198         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27199     } else {
27200         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27201     }
27202     
27203 };
27204
27205 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27206     /**
27207      * @cfg {Roo.data.Store} dataSource
27208      * The underlying data store providing the paged data
27209      */
27210     /**
27211      * @cfg {String/HTMLElement/Element} container
27212      * container The id or element that will contain the toolbar
27213      */
27214     /**
27215      * @cfg {Boolean} displayInfo
27216      * True to display the displayMsg (defaults to false)
27217      */
27218     /**
27219      * @cfg {Number} pageSize
27220      * The number of records to display per page (defaults to 20)
27221      */
27222     pageSize: 20,
27223     /**
27224      * @cfg {String} displayMsg
27225      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27226      */
27227     displayMsg : 'Displaying {0} - {1} of {2}',
27228     /**
27229      * @cfg {String} emptyMsg
27230      * The message to display when no records are found (defaults to "No data to display")
27231      */
27232     emptyMsg : 'No data to display',
27233     /**
27234      * Customizable piece of the default paging text (defaults to "Page")
27235      * @type String
27236      */
27237     beforePageText : "Page",
27238     /**
27239      * Customizable piece of the default paging text (defaults to "of %0")
27240      * @type String
27241      */
27242     afterPageText : "of {0}",
27243     /**
27244      * Customizable piece of the default paging text (defaults to "First Page")
27245      * @type String
27246      */
27247     firstText : "First Page",
27248     /**
27249      * Customizable piece of the default paging text (defaults to "Previous Page")
27250      * @type String
27251      */
27252     prevText : "Previous Page",
27253     /**
27254      * Customizable piece of the default paging text (defaults to "Next Page")
27255      * @type String
27256      */
27257     nextText : "Next Page",
27258     /**
27259      * Customizable piece of the default paging text (defaults to "Last Page")
27260      * @type String
27261      */
27262     lastText : "Last Page",
27263     /**
27264      * Customizable piece of the default paging text (defaults to "Refresh")
27265      * @type String
27266      */
27267     refreshText : "Refresh",
27268
27269     buttons : false,
27270     // private
27271     onRender : function(ct, position) 
27272     {
27273         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27274         this.navgroup.parentId = this.id;
27275         this.navgroup.onRender(this.el, null);
27276         // add the buttons to the navgroup
27277         
27278         if(this.displayInfo){
27279             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27280             this.displayEl = this.el.select('.x-paging-info', true).first();
27281 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27282 //            this.displayEl = navel.el.select('span',true).first();
27283         }
27284         
27285         var _this = this;
27286         
27287         if(this.buttons){
27288             Roo.each(_this.buttons, function(e){ // this might need to use render????
27289                Roo.factory(e).render(_this.el);
27290             });
27291         }
27292             
27293         Roo.each(_this.toolbarItems, function(e) {
27294             _this.navgroup.addItem(e);
27295         });
27296         
27297         
27298         this.first = this.navgroup.addItem({
27299             tooltip: this.firstText,
27300             cls: "prev btn-outline-secondary",
27301             html : ' <i class="fa fa-step-backward"></i>',
27302             disabled: true,
27303             preventDefault: true,
27304             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27305         });
27306         
27307         this.prev =  this.navgroup.addItem({
27308             tooltip: this.prevText,
27309             cls: "prev btn-outline-secondary",
27310             html : ' <i class="fa fa-backward"></i>',
27311             disabled: true,
27312             preventDefault: true,
27313             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27314         });
27315     //this.addSeparator();
27316         
27317         
27318         var field = this.navgroup.addItem( {
27319             tagtype : 'span',
27320             cls : 'x-paging-position  btn-outline-secondary',
27321              disabled: true,
27322             html : this.beforePageText  +
27323                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27324                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27325          } ); //?? escaped?
27326         
27327         this.field = field.el.select('input', true).first();
27328         this.field.on("keydown", this.onPagingKeydown, this);
27329         this.field.on("focus", function(){this.dom.select();});
27330     
27331     
27332         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27333         //this.field.setHeight(18);
27334         //this.addSeparator();
27335         this.next = this.navgroup.addItem({
27336             tooltip: this.nextText,
27337             cls: "next btn-outline-secondary",
27338             html : ' <i class="fa fa-forward"></i>',
27339             disabled: true,
27340             preventDefault: true,
27341             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27342         });
27343         this.last = this.navgroup.addItem({
27344             tooltip: this.lastText,
27345             html : ' <i class="fa fa-step-forward"></i>',
27346             cls: "next btn-outline-secondary",
27347             disabled: true,
27348             preventDefault: true,
27349             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27350         });
27351     //this.addSeparator();
27352         this.loading = this.navgroup.addItem({
27353             tooltip: this.refreshText,
27354             cls: "btn-outline-secondary",
27355             html : ' <i class="fa fa-refresh"></i>',
27356             preventDefault: true,
27357             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27358         });
27359         
27360     },
27361
27362     // private
27363     updateInfo : function(){
27364         if(this.displayEl){
27365             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27366             var msg = count == 0 ?
27367                 this.emptyMsg :
27368                 String.format(
27369                     this.displayMsg,
27370                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27371                 );
27372             this.displayEl.update(msg);
27373         }
27374     },
27375
27376     // private
27377     onLoad : function(ds, r, o)
27378     {
27379         this.cursor = o.params && o.params.start ? o.params.start : 0;
27380         
27381         var d = this.getPageData(),
27382             ap = d.activePage,
27383             ps = d.pages;
27384         
27385         
27386         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27387         this.field.dom.value = ap;
27388         this.first.setDisabled(ap == 1);
27389         this.prev.setDisabled(ap == 1);
27390         this.next.setDisabled(ap == ps);
27391         this.last.setDisabled(ap == ps);
27392         this.loading.enable();
27393         this.updateInfo();
27394     },
27395
27396     // private
27397     getPageData : function(){
27398         var total = this.ds.getTotalCount();
27399         return {
27400             total : total,
27401             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27402             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27403         };
27404     },
27405
27406     // private
27407     onLoadError : function(){
27408         this.loading.enable();
27409     },
27410
27411     // private
27412     onPagingKeydown : function(e){
27413         var k = e.getKey();
27414         var d = this.getPageData();
27415         if(k == e.RETURN){
27416             var v = this.field.dom.value, pageNum;
27417             if(!v || isNaN(pageNum = parseInt(v, 10))){
27418                 this.field.dom.value = d.activePage;
27419                 return;
27420             }
27421             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27422             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27423             e.stopEvent();
27424         }
27425         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))
27426         {
27427           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27428           this.field.dom.value = pageNum;
27429           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27430           e.stopEvent();
27431         }
27432         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27433         {
27434           var v = this.field.dom.value, pageNum; 
27435           var increment = (e.shiftKey) ? 10 : 1;
27436           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27437                 increment *= -1;
27438           }
27439           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27440             this.field.dom.value = d.activePage;
27441             return;
27442           }
27443           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27444           {
27445             this.field.dom.value = parseInt(v, 10) + increment;
27446             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27447             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27448           }
27449           e.stopEvent();
27450         }
27451     },
27452
27453     // private
27454     beforeLoad : function(){
27455         if(this.loading){
27456             this.loading.disable();
27457         }
27458     },
27459
27460     // private
27461     onClick : function(which){
27462         
27463         var ds = this.ds;
27464         if (!ds) {
27465             return;
27466         }
27467         
27468         switch(which){
27469             case "first":
27470                 ds.load({params:{start: 0, limit: this.pageSize}});
27471             break;
27472             case "prev":
27473                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27474             break;
27475             case "next":
27476                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27477             break;
27478             case "last":
27479                 var total = ds.getTotalCount();
27480                 var extra = total % this.pageSize;
27481                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27482                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27483             break;
27484             case "refresh":
27485                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27486             break;
27487         }
27488     },
27489
27490     /**
27491      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27492      * @param {Roo.data.Store} store The data store to unbind
27493      */
27494     unbind : function(ds){
27495         ds.un("beforeload", this.beforeLoad, this);
27496         ds.un("load", this.onLoad, this);
27497         ds.un("loadexception", this.onLoadError, this);
27498         ds.un("remove", this.updateInfo, this);
27499         ds.un("add", this.updateInfo, this);
27500         this.ds = undefined;
27501     },
27502
27503     /**
27504      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27505      * @param {Roo.data.Store} store The data store to bind
27506      */
27507     bind : function(ds){
27508         ds.on("beforeload", this.beforeLoad, this);
27509         ds.on("load", this.onLoad, this);
27510         ds.on("loadexception", this.onLoadError, this);
27511         ds.on("remove", this.updateInfo, this);
27512         ds.on("add", this.updateInfo, this);
27513         this.ds = ds;
27514     }
27515 });/*
27516  * - LGPL
27517  *
27518  * element
27519  * 
27520  */
27521
27522 /**
27523  * @class Roo.bootstrap.MessageBar
27524  * @extends Roo.bootstrap.Component
27525  * Bootstrap MessageBar class
27526  * @cfg {String} html contents of the MessageBar
27527  * @cfg {String} weight (info | success | warning | danger) default info
27528  * @cfg {String} beforeClass insert the bar before the given class
27529  * @cfg {Boolean} closable (true | false) default false
27530  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27531  * 
27532  * @constructor
27533  * Create a new Element
27534  * @param {Object} config The config object
27535  */
27536
27537 Roo.bootstrap.MessageBar = function(config){
27538     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27539 };
27540
27541 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27542     
27543     html: '',
27544     weight: 'info',
27545     closable: false,
27546     fixed: false,
27547     beforeClass: 'bootstrap-sticky-wrap',
27548     
27549     getAutoCreate : function(){
27550         
27551         var cfg = {
27552             tag: 'div',
27553             cls: 'alert alert-dismissable alert-' + this.weight,
27554             cn: [
27555                 {
27556                     tag: 'span',
27557                     cls: 'message',
27558                     html: this.html || ''
27559                 }
27560             ]
27561         };
27562         
27563         if(this.fixed){
27564             cfg.cls += ' alert-messages-fixed';
27565         }
27566         
27567         if(this.closable){
27568             cfg.cn.push({
27569                 tag: 'button',
27570                 cls: 'close',
27571                 html: 'x'
27572             });
27573         }
27574         
27575         return cfg;
27576     },
27577     
27578     onRender : function(ct, position)
27579     {
27580         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27581         
27582         if(!this.el){
27583             var cfg = Roo.apply({},  this.getAutoCreate());
27584             cfg.id = Roo.id();
27585             
27586             if (this.cls) {
27587                 cfg.cls += ' ' + this.cls;
27588             }
27589             if (this.style) {
27590                 cfg.style = this.style;
27591             }
27592             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27593             
27594             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27595         }
27596         
27597         this.el.select('>button.close').on('click', this.hide, this);
27598         
27599     },
27600     
27601     show : function()
27602     {
27603         if (!this.rendered) {
27604             this.render();
27605         }
27606         
27607         this.el.show();
27608         
27609         this.fireEvent('show', this);
27610         
27611     },
27612     
27613     hide : function()
27614     {
27615         if (!this.rendered) {
27616             this.render();
27617         }
27618         
27619         this.el.hide();
27620         
27621         this.fireEvent('hide', this);
27622     },
27623     
27624     update : function()
27625     {
27626 //        var e = this.el.dom.firstChild;
27627 //        
27628 //        if(this.closable){
27629 //            e = e.nextSibling;
27630 //        }
27631 //        
27632 //        e.data = this.html || '';
27633
27634         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27635     }
27636    
27637 });
27638
27639  
27640
27641      /*
27642  * - LGPL
27643  *
27644  * Graph
27645  * 
27646  */
27647
27648
27649 /**
27650  * @class Roo.bootstrap.Graph
27651  * @extends Roo.bootstrap.Component
27652  * Bootstrap Graph class
27653 > Prameters
27654  -sm {number} sm 4
27655  -md {number} md 5
27656  @cfg {String} graphtype  bar | vbar | pie
27657  @cfg {number} g_x coodinator | centre x (pie)
27658  @cfg {number} g_y coodinator | centre y (pie)
27659  @cfg {number} g_r radius (pie)
27660  @cfg {number} g_height height of the chart (respected by all elements in the set)
27661  @cfg {number} g_width width of the chart (respected by all elements in the set)
27662  @cfg {Object} title The title of the chart
27663     
27664  -{Array}  values
27665  -opts (object) options for the chart 
27666      o {
27667      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27668      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27669      o vgutter (number)
27670      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.
27671      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27672      o to
27673      o stretch (boolean)
27674      o }
27675  -opts (object) options for the pie
27676      o{
27677      o cut
27678      o startAngle (number)
27679      o endAngle (number)
27680      } 
27681  *
27682  * @constructor
27683  * Create a new Input
27684  * @param {Object} config The config object
27685  */
27686
27687 Roo.bootstrap.Graph = function(config){
27688     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27689     
27690     this.addEvents({
27691         // img events
27692         /**
27693          * @event click
27694          * The img click event for the img.
27695          * @param {Roo.EventObject} e
27696          */
27697         "click" : true
27698     });
27699 };
27700
27701 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27702     
27703     sm: 4,
27704     md: 5,
27705     graphtype: 'bar',
27706     g_height: 250,
27707     g_width: 400,
27708     g_x: 50,
27709     g_y: 50,
27710     g_r: 30,
27711     opts:{
27712         //g_colors: this.colors,
27713         g_type: 'soft',
27714         g_gutter: '20%'
27715
27716     },
27717     title : false,
27718
27719     getAutoCreate : function(){
27720         
27721         var cfg = {
27722             tag: 'div',
27723             html : null
27724         };
27725         
27726         
27727         return  cfg;
27728     },
27729
27730     onRender : function(ct,position){
27731         
27732         
27733         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27734         
27735         if (typeof(Raphael) == 'undefined') {
27736             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27737             return;
27738         }
27739         
27740         this.raphael = Raphael(this.el.dom);
27741         
27742                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27743                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27744                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27745                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27746                 /*
27747                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27748                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27749                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27750                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27751                 
27752                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27753                 r.barchart(330, 10, 300, 220, data1);
27754                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27755                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27756                 */
27757                 
27758                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27759                 // r.barchart(30, 30, 560, 250,  xdata, {
27760                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27761                 //     axis : "0 0 1 1",
27762                 //     axisxlabels :  xdata
27763                 //     //yvalues : cols,
27764                    
27765                 // });
27766 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27767 //        
27768 //        this.load(null,xdata,{
27769 //                axis : "0 0 1 1",
27770 //                axisxlabels :  xdata
27771 //                });
27772
27773     },
27774
27775     load : function(graphtype,xdata,opts)
27776     {
27777         this.raphael.clear();
27778         if(!graphtype) {
27779             graphtype = this.graphtype;
27780         }
27781         if(!opts){
27782             opts = this.opts;
27783         }
27784         var r = this.raphael,
27785             fin = function () {
27786                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27787             },
27788             fout = function () {
27789                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27790             },
27791             pfin = function() {
27792                 this.sector.stop();
27793                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27794
27795                 if (this.label) {
27796                     this.label[0].stop();
27797                     this.label[0].attr({ r: 7.5 });
27798                     this.label[1].attr({ "font-weight": 800 });
27799                 }
27800             },
27801             pfout = function() {
27802                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27803
27804                 if (this.label) {
27805                     this.label[0].animate({ r: 5 }, 500, "bounce");
27806                     this.label[1].attr({ "font-weight": 400 });
27807                 }
27808             };
27809
27810         switch(graphtype){
27811             case 'bar':
27812                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27813                 break;
27814             case 'hbar':
27815                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27816                 break;
27817             case 'pie':
27818 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27819 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27820 //            
27821                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27822                 
27823                 break;
27824
27825         }
27826         
27827         if(this.title){
27828             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27829         }
27830         
27831     },
27832     
27833     setTitle: function(o)
27834     {
27835         this.title = o;
27836     },
27837     
27838     initEvents: function() {
27839         
27840         if(!this.href){
27841             this.el.on('click', this.onClick, this);
27842         }
27843     },
27844     
27845     onClick : function(e)
27846     {
27847         Roo.log('img onclick');
27848         this.fireEvent('click', this, e);
27849     }
27850    
27851 });
27852
27853  
27854 /*
27855  * - LGPL
27856  *
27857  * numberBox
27858  * 
27859  */
27860 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27861
27862 /**
27863  * @class Roo.bootstrap.dash.NumberBox
27864  * @extends Roo.bootstrap.Component
27865  * Bootstrap NumberBox class
27866  * @cfg {String} headline Box headline
27867  * @cfg {String} content Box content
27868  * @cfg {String} icon Box icon
27869  * @cfg {String} footer Footer text
27870  * @cfg {String} fhref Footer href
27871  * 
27872  * @constructor
27873  * Create a new NumberBox
27874  * @param {Object} config The config object
27875  */
27876
27877
27878 Roo.bootstrap.dash.NumberBox = function(config){
27879     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27880     
27881 };
27882
27883 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27884     
27885     headline : '',
27886     content : '',
27887     icon : '',
27888     footer : '',
27889     fhref : '',
27890     ficon : '',
27891     
27892     getAutoCreate : function(){
27893         
27894         var cfg = {
27895             tag : 'div',
27896             cls : 'small-box ',
27897             cn : [
27898                 {
27899                     tag : 'div',
27900                     cls : 'inner',
27901                     cn :[
27902                         {
27903                             tag : 'h3',
27904                             cls : 'roo-headline',
27905                             html : this.headline
27906                         },
27907                         {
27908                             tag : 'p',
27909                             cls : 'roo-content',
27910                             html : this.content
27911                         }
27912                     ]
27913                 }
27914             ]
27915         };
27916         
27917         if(this.icon){
27918             cfg.cn.push({
27919                 tag : 'div',
27920                 cls : 'icon',
27921                 cn :[
27922                     {
27923                         tag : 'i',
27924                         cls : 'ion ' + this.icon
27925                     }
27926                 ]
27927             });
27928         }
27929         
27930         if(this.footer){
27931             var footer = {
27932                 tag : 'a',
27933                 cls : 'small-box-footer',
27934                 href : this.fhref || '#',
27935                 html : this.footer
27936             };
27937             
27938             cfg.cn.push(footer);
27939             
27940         }
27941         
27942         return  cfg;
27943     },
27944
27945     onRender : function(ct,position){
27946         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27947
27948
27949        
27950                 
27951     },
27952
27953     setHeadline: function (value)
27954     {
27955         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27956     },
27957     
27958     setFooter: function (value, href)
27959     {
27960         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27961         
27962         if(href){
27963             this.el.select('a.small-box-footer',true).first().attr('href', href);
27964         }
27965         
27966     },
27967
27968     setContent: function (value)
27969     {
27970         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27971     },
27972
27973     initEvents: function() 
27974     {   
27975         
27976     }
27977     
27978 });
27979
27980  
27981 /*
27982  * - LGPL
27983  *
27984  * TabBox
27985  * 
27986  */
27987 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27988
27989 /**
27990  * @class Roo.bootstrap.dash.TabBox
27991  * @extends Roo.bootstrap.Component
27992  * Bootstrap TabBox class
27993  * @cfg {String} title Title of the TabBox
27994  * @cfg {String} icon Icon of the TabBox
27995  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27996  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27997  * 
27998  * @constructor
27999  * Create a new TabBox
28000  * @param {Object} config The config object
28001  */
28002
28003
28004 Roo.bootstrap.dash.TabBox = function(config){
28005     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28006     this.addEvents({
28007         // raw events
28008         /**
28009          * @event addpane
28010          * When a pane is added
28011          * @param {Roo.bootstrap.dash.TabPane} pane
28012          */
28013         "addpane" : true,
28014         /**
28015          * @event activatepane
28016          * When a pane is activated
28017          * @param {Roo.bootstrap.dash.TabPane} pane
28018          */
28019         "activatepane" : true
28020         
28021          
28022     });
28023     
28024     this.panes = [];
28025 };
28026
28027 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28028
28029     title : '',
28030     icon : false,
28031     showtabs : true,
28032     tabScrollable : false,
28033     
28034     getChildContainer : function()
28035     {
28036         return this.el.select('.tab-content', true).first();
28037     },
28038     
28039     getAutoCreate : function(){
28040         
28041         var header = {
28042             tag: 'li',
28043             cls: 'pull-left header',
28044             html: this.title,
28045             cn : []
28046         };
28047         
28048         if(this.icon){
28049             header.cn.push({
28050                 tag: 'i',
28051                 cls: 'fa ' + this.icon
28052             });
28053         }
28054         
28055         var h = {
28056             tag: 'ul',
28057             cls: 'nav nav-tabs pull-right',
28058             cn: [
28059                 header
28060             ]
28061         };
28062         
28063         if(this.tabScrollable){
28064             h = {
28065                 tag: 'div',
28066                 cls: 'tab-header',
28067                 cn: [
28068                     {
28069                         tag: 'ul',
28070                         cls: 'nav nav-tabs pull-right',
28071                         cn: [
28072                             header
28073                         ]
28074                     }
28075                 ]
28076             };
28077         }
28078         
28079         var cfg = {
28080             tag: 'div',
28081             cls: 'nav-tabs-custom',
28082             cn: [
28083                 h,
28084                 {
28085                     tag: 'div',
28086                     cls: 'tab-content no-padding',
28087                     cn: []
28088                 }
28089             ]
28090         };
28091
28092         return  cfg;
28093     },
28094     initEvents : function()
28095     {
28096         //Roo.log('add add pane handler');
28097         this.on('addpane', this.onAddPane, this);
28098     },
28099      /**
28100      * Updates the box title
28101      * @param {String} html to set the title to.
28102      */
28103     setTitle : function(value)
28104     {
28105         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28106     },
28107     onAddPane : function(pane)
28108     {
28109         this.panes.push(pane);
28110         //Roo.log('addpane');
28111         //Roo.log(pane);
28112         // tabs are rendere left to right..
28113         if(!this.showtabs){
28114             return;
28115         }
28116         
28117         var ctr = this.el.select('.nav-tabs', true).first();
28118          
28119          
28120         var existing = ctr.select('.nav-tab',true);
28121         var qty = existing.getCount();;
28122         
28123         
28124         var tab = ctr.createChild({
28125             tag : 'li',
28126             cls : 'nav-tab' + (qty ? '' : ' active'),
28127             cn : [
28128                 {
28129                     tag : 'a',
28130                     href:'#',
28131                     html : pane.title
28132                 }
28133             ]
28134         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28135         pane.tab = tab;
28136         
28137         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28138         if (!qty) {
28139             pane.el.addClass('active');
28140         }
28141         
28142                 
28143     },
28144     onTabClick : function(ev,un,ob,pane)
28145     {
28146         //Roo.log('tab - prev default');
28147         ev.preventDefault();
28148         
28149         
28150         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28151         pane.tab.addClass('active');
28152         //Roo.log(pane.title);
28153         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28154         // technically we should have a deactivate event.. but maybe add later.
28155         // and it should not de-activate the selected tab...
28156         this.fireEvent('activatepane', pane);
28157         pane.el.addClass('active');
28158         pane.fireEvent('activate');
28159         
28160         
28161     },
28162     
28163     getActivePane : function()
28164     {
28165         var r = false;
28166         Roo.each(this.panes, function(p) {
28167             if(p.el.hasClass('active')){
28168                 r = p;
28169                 return false;
28170             }
28171             
28172             return;
28173         });
28174         
28175         return r;
28176     }
28177     
28178     
28179 });
28180
28181  
28182 /*
28183  * - LGPL
28184  *
28185  * Tab pane
28186  * 
28187  */
28188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28189 /**
28190  * @class Roo.bootstrap.TabPane
28191  * @extends Roo.bootstrap.Component
28192  * Bootstrap TabPane class
28193  * @cfg {Boolean} active (false | true) Default false
28194  * @cfg {String} title title of panel
28195
28196  * 
28197  * @constructor
28198  * Create a new TabPane
28199  * @param {Object} config The config object
28200  */
28201
28202 Roo.bootstrap.dash.TabPane = function(config){
28203     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28204     
28205     this.addEvents({
28206         // raw events
28207         /**
28208          * @event activate
28209          * When a pane is activated
28210          * @param {Roo.bootstrap.dash.TabPane} pane
28211          */
28212         "activate" : true
28213          
28214     });
28215 };
28216
28217 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28218     
28219     active : false,
28220     title : '',
28221     
28222     // the tabBox that this is attached to.
28223     tab : false,
28224      
28225     getAutoCreate : function() 
28226     {
28227         var cfg = {
28228             tag: 'div',
28229             cls: 'tab-pane'
28230         };
28231         
28232         if(this.active){
28233             cfg.cls += ' active';
28234         }
28235         
28236         return cfg;
28237     },
28238     initEvents  : function()
28239     {
28240         //Roo.log('trigger add pane handler');
28241         this.parent().fireEvent('addpane', this)
28242     },
28243     
28244      /**
28245      * Updates the tab title 
28246      * @param {String} html to set the title to.
28247      */
28248     setTitle: function(str)
28249     {
28250         if (!this.tab) {
28251             return;
28252         }
28253         this.title = str;
28254         this.tab.select('a', true).first().dom.innerHTML = str;
28255         
28256     }
28257     
28258     
28259     
28260 });
28261
28262  
28263
28264
28265  /*
28266  * - LGPL
28267  *
28268  * menu
28269  * 
28270  */
28271 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28272
28273 /**
28274  * @class Roo.bootstrap.menu.Menu
28275  * @extends Roo.bootstrap.Component
28276  * Bootstrap Menu class - container for Menu
28277  * @cfg {String} html Text of the menu
28278  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28279  * @cfg {String} icon Font awesome icon
28280  * @cfg {String} pos Menu align to (top | bottom) default bottom
28281  * 
28282  * 
28283  * @constructor
28284  * Create a new Menu
28285  * @param {Object} config The config object
28286  */
28287
28288
28289 Roo.bootstrap.menu.Menu = function(config){
28290     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28291     
28292     this.addEvents({
28293         /**
28294          * @event beforeshow
28295          * Fires before this menu is displayed
28296          * @param {Roo.bootstrap.menu.Menu} this
28297          */
28298         beforeshow : true,
28299         /**
28300          * @event beforehide
28301          * Fires before this menu is hidden
28302          * @param {Roo.bootstrap.menu.Menu} this
28303          */
28304         beforehide : true,
28305         /**
28306          * @event show
28307          * Fires after this menu is displayed
28308          * @param {Roo.bootstrap.menu.Menu} this
28309          */
28310         show : true,
28311         /**
28312          * @event hide
28313          * Fires after this menu is hidden
28314          * @param {Roo.bootstrap.menu.Menu} this
28315          */
28316         hide : true,
28317         /**
28318          * @event click
28319          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28320          * @param {Roo.bootstrap.menu.Menu} this
28321          * @param {Roo.EventObject} e
28322          */
28323         click : true
28324     });
28325     
28326 };
28327
28328 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28329     
28330     submenu : false,
28331     html : '',
28332     weight : 'default',
28333     icon : false,
28334     pos : 'bottom',
28335     
28336     
28337     getChildContainer : function() {
28338         if(this.isSubMenu){
28339             return this.el;
28340         }
28341         
28342         return this.el.select('ul.dropdown-menu', true).first();  
28343     },
28344     
28345     getAutoCreate : function()
28346     {
28347         var text = [
28348             {
28349                 tag : 'span',
28350                 cls : 'roo-menu-text',
28351                 html : this.html
28352             }
28353         ];
28354         
28355         if(this.icon){
28356             text.unshift({
28357                 tag : 'i',
28358                 cls : 'fa ' + this.icon
28359             })
28360         }
28361         
28362         
28363         var cfg = {
28364             tag : 'div',
28365             cls : 'btn-group',
28366             cn : [
28367                 {
28368                     tag : 'button',
28369                     cls : 'dropdown-button btn btn-' + this.weight,
28370                     cn : text
28371                 },
28372                 {
28373                     tag : 'button',
28374                     cls : 'dropdown-toggle btn btn-' + this.weight,
28375                     cn : [
28376                         {
28377                             tag : 'span',
28378                             cls : 'caret'
28379                         }
28380                     ]
28381                 },
28382                 {
28383                     tag : 'ul',
28384                     cls : 'dropdown-menu'
28385                 }
28386             ]
28387             
28388         };
28389         
28390         if(this.pos == 'top'){
28391             cfg.cls += ' dropup';
28392         }
28393         
28394         if(this.isSubMenu){
28395             cfg = {
28396                 tag : 'ul',
28397                 cls : 'dropdown-menu'
28398             }
28399         }
28400         
28401         return cfg;
28402     },
28403     
28404     onRender : function(ct, position)
28405     {
28406         this.isSubMenu = ct.hasClass('dropdown-submenu');
28407         
28408         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28409     },
28410     
28411     initEvents : function() 
28412     {
28413         if(this.isSubMenu){
28414             return;
28415         }
28416         
28417         this.hidden = true;
28418         
28419         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28420         this.triggerEl.on('click', this.onTriggerPress, this);
28421         
28422         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28423         this.buttonEl.on('click', this.onClick, this);
28424         
28425     },
28426     
28427     list : function()
28428     {
28429         if(this.isSubMenu){
28430             return this.el;
28431         }
28432         
28433         return this.el.select('ul.dropdown-menu', true).first();
28434     },
28435     
28436     onClick : function(e)
28437     {
28438         this.fireEvent("click", this, e);
28439     },
28440     
28441     onTriggerPress  : function(e)
28442     {   
28443         if (this.isVisible()) {
28444             this.hide();
28445         } else {
28446             this.show();
28447         }
28448     },
28449     
28450     isVisible : function(){
28451         return !this.hidden;
28452     },
28453     
28454     show : function()
28455     {
28456         this.fireEvent("beforeshow", this);
28457         
28458         this.hidden = false;
28459         this.el.addClass('open');
28460         
28461         Roo.get(document).on("mouseup", this.onMouseUp, this);
28462         
28463         this.fireEvent("show", this);
28464         
28465         
28466     },
28467     
28468     hide : function()
28469     {
28470         this.fireEvent("beforehide", this);
28471         
28472         this.hidden = true;
28473         this.el.removeClass('open');
28474         
28475         Roo.get(document).un("mouseup", this.onMouseUp);
28476         
28477         this.fireEvent("hide", this);
28478     },
28479     
28480     onMouseUp : function()
28481     {
28482         this.hide();
28483     }
28484     
28485 });
28486
28487  
28488  /*
28489  * - LGPL
28490  *
28491  * menu item
28492  * 
28493  */
28494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28495
28496 /**
28497  * @class Roo.bootstrap.menu.Item
28498  * @extends Roo.bootstrap.Component
28499  * Bootstrap MenuItem class
28500  * @cfg {Boolean} submenu (true | false) default false
28501  * @cfg {String} html text of the item
28502  * @cfg {String} href the link
28503  * @cfg {Boolean} disable (true | false) default false
28504  * @cfg {Boolean} preventDefault (true | false) default true
28505  * @cfg {String} icon Font awesome icon
28506  * @cfg {String} pos Submenu align to (left | right) default right 
28507  * 
28508  * 
28509  * @constructor
28510  * Create a new Item
28511  * @param {Object} config The config object
28512  */
28513
28514
28515 Roo.bootstrap.menu.Item = function(config){
28516     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28517     this.addEvents({
28518         /**
28519          * @event mouseover
28520          * Fires when the mouse is hovering over this menu
28521          * @param {Roo.bootstrap.menu.Item} this
28522          * @param {Roo.EventObject} e
28523          */
28524         mouseover : true,
28525         /**
28526          * @event mouseout
28527          * Fires when the mouse exits this menu
28528          * @param {Roo.bootstrap.menu.Item} this
28529          * @param {Roo.EventObject} e
28530          */
28531         mouseout : true,
28532         // raw events
28533         /**
28534          * @event click
28535          * The raw click event for the entire grid.
28536          * @param {Roo.EventObject} e
28537          */
28538         click : true
28539     });
28540 };
28541
28542 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28543     
28544     submenu : false,
28545     href : '',
28546     html : '',
28547     preventDefault: true,
28548     disable : false,
28549     icon : false,
28550     pos : 'right',
28551     
28552     getAutoCreate : function()
28553     {
28554         var text = [
28555             {
28556                 tag : 'span',
28557                 cls : 'roo-menu-item-text',
28558                 html : this.html
28559             }
28560         ];
28561         
28562         if(this.icon){
28563             text.unshift({
28564                 tag : 'i',
28565                 cls : 'fa ' + this.icon
28566             })
28567         }
28568         
28569         var cfg = {
28570             tag : 'li',
28571             cn : [
28572                 {
28573                     tag : 'a',
28574                     href : this.href || '#',
28575                     cn : text
28576                 }
28577             ]
28578         };
28579         
28580         if(this.disable){
28581             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28582         }
28583         
28584         if(this.submenu){
28585             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28586             
28587             if(this.pos == 'left'){
28588                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28589             }
28590         }
28591         
28592         return cfg;
28593     },
28594     
28595     initEvents : function() 
28596     {
28597         this.el.on('mouseover', this.onMouseOver, this);
28598         this.el.on('mouseout', this.onMouseOut, this);
28599         
28600         this.el.select('a', true).first().on('click', this.onClick, this);
28601         
28602     },
28603     
28604     onClick : function(e)
28605     {
28606         if(this.preventDefault){
28607             e.preventDefault();
28608         }
28609         
28610         this.fireEvent("click", this, e);
28611     },
28612     
28613     onMouseOver : function(e)
28614     {
28615         if(this.submenu && this.pos == 'left'){
28616             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28617         }
28618         
28619         this.fireEvent("mouseover", this, e);
28620     },
28621     
28622     onMouseOut : function(e)
28623     {
28624         this.fireEvent("mouseout", this, e);
28625     }
28626 });
28627
28628  
28629
28630  /*
28631  * - LGPL
28632  *
28633  * menu separator
28634  * 
28635  */
28636 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28637
28638 /**
28639  * @class Roo.bootstrap.menu.Separator
28640  * @extends Roo.bootstrap.Component
28641  * Bootstrap Separator class
28642  * 
28643  * @constructor
28644  * Create a new Separator
28645  * @param {Object} config The config object
28646  */
28647
28648
28649 Roo.bootstrap.menu.Separator = function(config){
28650     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28651 };
28652
28653 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28654     
28655     getAutoCreate : function(){
28656         var cfg = {
28657             tag : 'li',
28658             cls: 'divider'
28659         };
28660         
28661         return cfg;
28662     }
28663    
28664 });
28665
28666  
28667
28668  /*
28669  * - LGPL
28670  *
28671  * Tooltip
28672  * 
28673  */
28674
28675 /**
28676  * @class Roo.bootstrap.Tooltip
28677  * Bootstrap Tooltip class
28678  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28679  * to determine which dom element triggers the tooltip.
28680  * 
28681  * It needs to add support for additional attributes like tooltip-position
28682  * 
28683  * @constructor
28684  * Create a new Toolti
28685  * @param {Object} config The config object
28686  */
28687
28688 Roo.bootstrap.Tooltip = function(config){
28689     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28690     
28691     this.alignment = Roo.bootstrap.Tooltip.alignment;
28692     
28693     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28694         this.alignment = config.alignment;
28695     }
28696     
28697 };
28698
28699 Roo.apply(Roo.bootstrap.Tooltip, {
28700     /**
28701      * @function init initialize tooltip monitoring.
28702      * @static
28703      */
28704     currentEl : false,
28705     currentTip : false,
28706     currentRegion : false,
28707     
28708     //  init : delay?
28709     
28710     init : function()
28711     {
28712         Roo.get(document).on('mouseover', this.enter ,this);
28713         Roo.get(document).on('mouseout', this.leave, this);
28714          
28715         
28716         this.currentTip = new Roo.bootstrap.Tooltip();
28717     },
28718     
28719     enter : function(ev)
28720     {
28721         var dom = ev.getTarget();
28722         
28723         //Roo.log(['enter',dom]);
28724         var el = Roo.fly(dom);
28725         if (this.currentEl) {
28726             //Roo.log(dom);
28727             //Roo.log(this.currentEl);
28728             //Roo.log(this.currentEl.contains(dom));
28729             if (this.currentEl == el) {
28730                 return;
28731             }
28732             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28733                 return;
28734             }
28735
28736         }
28737         
28738         if (this.currentTip.el) {
28739             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28740         }    
28741         //Roo.log(ev);
28742         
28743         if(!el || el.dom == document){
28744             return;
28745         }
28746         
28747         var bindEl = el;
28748         
28749         // you can not look for children, as if el is the body.. then everythign is the child..
28750         if (!el.attr('tooltip')) { //
28751             if (!el.select("[tooltip]").elements.length) {
28752                 return;
28753             }
28754             // is the mouse over this child...?
28755             bindEl = el.select("[tooltip]").first();
28756             var xy = ev.getXY();
28757             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28758                 //Roo.log("not in region.");
28759                 return;
28760             }
28761             //Roo.log("child element over..");
28762             
28763         }
28764         this.currentEl = bindEl;
28765         this.currentTip.bind(bindEl);
28766         this.currentRegion = Roo.lib.Region.getRegion(dom);
28767         this.currentTip.enter();
28768         
28769     },
28770     leave : function(ev)
28771     {
28772         var dom = ev.getTarget();
28773         //Roo.log(['leave',dom]);
28774         if (!this.currentEl) {
28775             return;
28776         }
28777         
28778         
28779         if (dom != this.currentEl.dom) {
28780             return;
28781         }
28782         var xy = ev.getXY();
28783         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28784             return;
28785         }
28786         // only activate leave if mouse cursor is outside... bounding box..
28787         
28788         
28789         
28790         
28791         if (this.currentTip) {
28792             this.currentTip.leave();
28793         }
28794         //Roo.log('clear currentEl');
28795         this.currentEl = false;
28796         
28797         
28798     },
28799     alignment : {
28800         'left' : ['r-l', [-2,0], 'right'],
28801         'right' : ['l-r', [2,0], 'left'],
28802         'bottom' : ['t-b', [0,2], 'top'],
28803         'top' : [ 'b-t', [0,-2], 'bottom']
28804     }
28805     
28806 });
28807
28808
28809 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28810     
28811     
28812     bindEl : false,
28813     
28814     delay : null, // can be { show : 300 , hide: 500}
28815     
28816     timeout : null,
28817     
28818     hoverState : null, //???
28819     
28820     placement : 'bottom', 
28821     
28822     alignment : false,
28823     
28824     getAutoCreate : function(){
28825     
28826         var cfg = {
28827            cls : 'tooltip',   
28828            role : 'tooltip',
28829            cn : [
28830                 {
28831                     cls : 'tooltip-arrow arrow'
28832                 },
28833                 {
28834                     cls : 'tooltip-inner'
28835                 }
28836            ]
28837         };
28838         
28839         return cfg;
28840     },
28841     bind : function(el)
28842     {
28843         this.bindEl = el;
28844     },
28845     
28846     initEvents : function()
28847     {
28848         this.arrowEl = this.el.select('.arrow', true).first();
28849         this.innerEl = this.el.select('.tooltip-inner', true).first();
28850     },
28851     
28852     enter : function () {
28853        
28854         if (this.timeout != null) {
28855             clearTimeout(this.timeout);
28856         }
28857         
28858         this.hoverState = 'in';
28859          //Roo.log("enter - show");
28860         if (!this.delay || !this.delay.show) {
28861             this.show();
28862             return;
28863         }
28864         var _t = this;
28865         this.timeout = setTimeout(function () {
28866             if (_t.hoverState == 'in') {
28867                 _t.show();
28868             }
28869         }, this.delay.show);
28870     },
28871     leave : function()
28872     {
28873         clearTimeout(this.timeout);
28874     
28875         this.hoverState = 'out';
28876          if (!this.delay || !this.delay.hide) {
28877             this.hide();
28878             return;
28879         }
28880        
28881         var _t = this;
28882         this.timeout = setTimeout(function () {
28883             //Roo.log("leave - timeout");
28884             
28885             if (_t.hoverState == 'out') {
28886                 _t.hide();
28887                 Roo.bootstrap.Tooltip.currentEl = false;
28888             }
28889         }, delay);
28890     },
28891     
28892     show : function (msg)
28893     {
28894         if (!this.el) {
28895             this.render(document.body);
28896         }
28897         // set content.
28898         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28899         
28900         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28901         
28902         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28903         
28904         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28905                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28906         
28907         var placement = typeof this.placement == 'function' ?
28908             this.placement.call(this, this.el, on_el) :
28909             this.placement;
28910             
28911         var autoToken = /\s?auto?\s?/i;
28912         var autoPlace = autoToken.test(placement);
28913         if (autoPlace) {
28914             placement = placement.replace(autoToken, '') || 'top';
28915         }
28916         
28917         //this.el.detach()
28918         //this.el.setXY([0,0]);
28919         this.el.show();
28920         //this.el.dom.style.display='block';
28921         
28922         //this.el.appendTo(on_el);
28923         
28924         var p = this.getPosition();
28925         var box = this.el.getBox();
28926         
28927         if (autoPlace) {
28928             // fixme..
28929         }
28930         
28931         var align = this.alignment[placement];
28932         
28933         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28934         
28935         if(placement == 'top' || placement == 'bottom'){
28936             if(xy[0] < 0){
28937                 placement = 'right';
28938             }
28939             
28940             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28941                 placement = 'left';
28942             }
28943             
28944             var scroll = Roo.select('body', true).first().getScroll();
28945             
28946             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28947                 placement = 'top';
28948             }
28949             
28950             align = this.alignment[placement];
28951             
28952             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28953             
28954         }
28955         
28956         this.el.alignTo(this.bindEl, align[0],align[1]);
28957         //var arrow = this.el.select('.arrow',true).first();
28958         //arrow.set(align[2], 
28959         
28960         this.el.addClass(placement);
28961         this.el.addClass("bs-tooltip-"+ placement);
28962         
28963         this.el.addClass('in fade show');
28964         
28965         this.hoverState = null;
28966         
28967         if (this.el.hasClass('fade')) {
28968             // fade it?
28969         }
28970         
28971         
28972         
28973         
28974         
28975     },
28976     hide : function()
28977     {
28978          
28979         if (!this.el) {
28980             return;
28981         }
28982         //this.el.setXY([0,0]);
28983         this.el.removeClass(['show', 'in']);
28984         //this.el.hide();
28985         
28986     }
28987     
28988 });
28989  
28990
28991  /*
28992  * - LGPL
28993  *
28994  * Location Picker
28995  * 
28996  */
28997
28998 /**
28999  * @class Roo.bootstrap.LocationPicker
29000  * @extends Roo.bootstrap.Component
29001  * Bootstrap LocationPicker class
29002  * @cfg {Number} latitude Position when init default 0
29003  * @cfg {Number} longitude Position when init default 0
29004  * @cfg {Number} zoom default 15
29005  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29006  * @cfg {Boolean} mapTypeControl default false
29007  * @cfg {Boolean} disableDoubleClickZoom default false
29008  * @cfg {Boolean} scrollwheel default true
29009  * @cfg {Boolean} streetViewControl default false
29010  * @cfg {Number} radius default 0
29011  * @cfg {String} locationName
29012  * @cfg {Boolean} draggable default true
29013  * @cfg {Boolean} enableAutocomplete default false
29014  * @cfg {Boolean} enableReverseGeocode default true
29015  * @cfg {String} markerTitle
29016  * 
29017  * @constructor
29018  * Create a new LocationPicker
29019  * @param {Object} config The config object
29020  */
29021
29022
29023 Roo.bootstrap.LocationPicker = function(config){
29024     
29025     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29026     
29027     this.addEvents({
29028         /**
29029          * @event initial
29030          * Fires when the picker initialized.
29031          * @param {Roo.bootstrap.LocationPicker} this
29032          * @param {Google Location} location
29033          */
29034         initial : true,
29035         /**
29036          * @event positionchanged
29037          * Fires when the picker position changed.
29038          * @param {Roo.bootstrap.LocationPicker} this
29039          * @param {Google Location} location
29040          */
29041         positionchanged : true,
29042         /**
29043          * @event resize
29044          * Fires when the map resize.
29045          * @param {Roo.bootstrap.LocationPicker} this
29046          */
29047         resize : true,
29048         /**
29049          * @event show
29050          * Fires when the map show.
29051          * @param {Roo.bootstrap.LocationPicker} this
29052          */
29053         show : true,
29054         /**
29055          * @event hide
29056          * Fires when the map hide.
29057          * @param {Roo.bootstrap.LocationPicker} this
29058          */
29059         hide : true,
29060         /**
29061          * @event mapClick
29062          * Fires when click the map.
29063          * @param {Roo.bootstrap.LocationPicker} this
29064          * @param {Map event} e
29065          */
29066         mapClick : true,
29067         /**
29068          * @event mapRightClick
29069          * Fires when right click the map.
29070          * @param {Roo.bootstrap.LocationPicker} this
29071          * @param {Map event} e
29072          */
29073         mapRightClick : true,
29074         /**
29075          * @event markerClick
29076          * Fires when click the marker.
29077          * @param {Roo.bootstrap.LocationPicker} this
29078          * @param {Map event} e
29079          */
29080         markerClick : true,
29081         /**
29082          * @event markerRightClick
29083          * Fires when right click the marker.
29084          * @param {Roo.bootstrap.LocationPicker} this
29085          * @param {Map event} e
29086          */
29087         markerRightClick : true,
29088         /**
29089          * @event OverlayViewDraw
29090          * Fires when OverlayView Draw
29091          * @param {Roo.bootstrap.LocationPicker} this
29092          */
29093         OverlayViewDraw : true,
29094         /**
29095          * @event OverlayViewOnAdd
29096          * Fires when OverlayView Draw
29097          * @param {Roo.bootstrap.LocationPicker} this
29098          */
29099         OverlayViewOnAdd : true,
29100         /**
29101          * @event OverlayViewOnRemove
29102          * Fires when OverlayView Draw
29103          * @param {Roo.bootstrap.LocationPicker} this
29104          */
29105         OverlayViewOnRemove : true,
29106         /**
29107          * @event OverlayViewShow
29108          * Fires when OverlayView Draw
29109          * @param {Roo.bootstrap.LocationPicker} this
29110          * @param {Pixel} cpx
29111          */
29112         OverlayViewShow : true,
29113         /**
29114          * @event OverlayViewHide
29115          * Fires when OverlayView Draw
29116          * @param {Roo.bootstrap.LocationPicker} this
29117          */
29118         OverlayViewHide : true,
29119         /**
29120          * @event loadexception
29121          * Fires when load google lib failed.
29122          * @param {Roo.bootstrap.LocationPicker} this
29123          */
29124         loadexception : true
29125     });
29126         
29127 };
29128
29129 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29130     
29131     gMapContext: false,
29132     
29133     latitude: 0,
29134     longitude: 0,
29135     zoom: 15,
29136     mapTypeId: false,
29137     mapTypeControl: false,
29138     disableDoubleClickZoom: false,
29139     scrollwheel: true,
29140     streetViewControl: false,
29141     radius: 0,
29142     locationName: '',
29143     draggable: true,
29144     enableAutocomplete: false,
29145     enableReverseGeocode: true,
29146     markerTitle: '',
29147     
29148     getAutoCreate: function()
29149     {
29150
29151         var cfg = {
29152             tag: 'div',
29153             cls: 'roo-location-picker'
29154         };
29155         
29156         return cfg
29157     },
29158     
29159     initEvents: function(ct, position)
29160     {       
29161         if(!this.el.getWidth() || this.isApplied()){
29162             return;
29163         }
29164         
29165         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29166         
29167         this.initial();
29168     },
29169     
29170     initial: function()
29171     {
29172         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29173             this.fireEvent('loadexception', this);
29174             return;
29175         }
29176         
29177         if(!this.mapTypeId){
29178             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29179         }
29180         
29181         this.gMapContext = this.GMapContext();
29182         
29183         this.initOverlayView();
29184         
29185         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29186         
29187         var _this = this;
29188                 
29189         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29190             _this.setPosition(_this.gMapContext.marker.position);
29191         });
29192         
29193         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29194             _this.fireEvent('mapClick', this, event);
29195             
29196         });
29197
29198         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29199             _this.fireEvent('mapRightClick', this, event);
29200             
29201         });
29202         
29203         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29204             _this.fireEvent('markerClick', this, event);
29205             
29206         });
29207
29208         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29209             _this.fireEvent('markerRightClick', this, event);
29210             
29211         });
29212         
29213         this.setPosition(this.gMapContext.location);
29214         
29215         this.fireEvent('initial', this, this.gMapContext.location);
29216     },
29217     
29218     initOverlayView: function()
29219     {
29220         var _this = this;
29221         
29222         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29223             
29224             draw: function()
29225             {
29226                 _this.fireEvent('OverlayViewDraw', _this);
29227             },
29228             
29229             onAdd: function()
29230             {
29231                 _this.fireEvent('OverlayViewOnAdd', _this);
29232             },
29233             
29234             onRemove: function()
29235             {
29236                 _this.fireEvent('OverlayViewOnRemove', _this);
29237             },
29238             
29239             show: function(cpx)
29240             {
29241                 _this.fireEvent('OverlayViewShow', _this, cpx);
29242             },
29243             
29244             hide: function()
29245             {
29246                 _this.fireEvent('OverlayViewHide', _this);
29247             }
29248             
29249         });
29250     },
29251     
29252     fromLatLngToContainerPixel: function(event)
29253     {
29254         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29255     },
29256     
29257     isApplied: function() 
29258     {
29259         return this.getGmapContext() == false ? false : true;
29260     },
29261     
29262     getGmapContext: function() 
29263     {
29264         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29265     },
29266     
29267     GMapContext: function() 
29268     {
29269         var position = new google.maps.LatLng(this.latitude, this.longitude);
29270         
29271         var _map = new google.maps.Map(this.el.dom, {
29272             center: position,
29273             zoom: this.zoom,
29274             mapTypeId: this.mapTypeId,
29275             mapTypeControl: this.mapTypeControl,
29276             disableDoubleClickZoom: this.disableDoubleClickZoom,
29277             scrollwheel: this.scrollwheel,
29278             streetViewControl: this.streetViewControl,
29279             locationName: this.locationName,
29280             draggable: this.draggable,
29281             enableAutocomplete: this.enableAutocomplete,
29282             enableReverseGeocode: this.enableReverseGeocode
29283         });
29284         
29285         var _marker = new google.maps.Marker({
29286             position: position,
29287             map: _map,
29288             title: this.markerTitle,
29289             draggable: this.draggable
29290         });
29291         
29292         return {
29293             map: _map,
29294             marker: _marker,
29295             circle: null,
29296             location: position,
29297             radius: this.radius,
29298             locationName: this.locationName,
29299             addressComponents: {
29300                 formatted_address: null,
29301                 addressLine1: null,
29302                 addressLine2: null,
29303                 streetName: null,
29304                 streetNumber: null,
29305                 city: null,
29306                 district: null,
29307                 state: null,
29308                 stateOrProvince: null
29309             },
29310             settings: this,
29311             domContainer: this.el.dom,
29312             geodecoder: new google.maps.Geocoder()
29313         };
29314     },
29315     
29316     drawCircle: function(center, radius, options) 
29317     {
29318         if (this.gMapContext.circle != null) {
29319             this.gMapContext.circle.setMap(null);
29320         }
29321         if (radius > 0) {
29322             radius *= 1;
29323             options = Roo.apply({}, options, {
29324                 strokeColor: "#0000FF",
29325                 strokeOpacity: .35,
29326                 strokeWeight: 2,
29327                 fillColor: "#0000FF",
29328                 fillOpacity: .2
29329             });
29330             
29331             options.map = this.gMapContext.map;
29332             options.radius = radius;
29333             options.center = center;
29334             this.gMapContext.circle = new google.maps.Circle(options);
29335             return this.gMapContext.circle;
29336         }
29337         
29338         return null;
29339     },
29340     
29341     setPosition: function(location) 
29342     {
29343         this.gMapContext.location = location;
29344         this.gMapContext.marker.setPosition(location);
29345         this.gMapContext.map.panTo(location);
29346         this.drawCircle(location, this.gMapContext.radius, {});
29347         
29348         var _this = this;
29349         
29350         if (this.gMapContext.settings.enableReverseGeocode) {
29351             this.gMapContext.geodecoder.geocode({
29352                 latLng: this.gMapContext.location
29353             }, function(results, status) {
29354                 
29355                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29356                     _this.gMapContext.locationName = results[0].formatted_address;
29357                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29358                     
29359                     _this.fireEvent('positionchanged', this, location);
29360                 }
29361             });
29362             
29363             return;
29364         }
29365         
29366         this.fireEvent('positionchanged', this, location);
29367     },
29368     
29369     resize: function()
29370     {
29371         google.maps.event.trigger(this.gMapContext.map, "resize");
29372         
29373         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29374         
29375         this.fireEvent('resize', this);
29376     },
29377     
29378     setPositionByLatLng: function(latitude, longitude)
29379     {
29380         this.setPosition(new google.maps.LatLng(latitude, longitude));
29381     },
29382     
29383     getCurrentPosition: function() 
29384     {
29385         return {
29386             latitude: this.gMapContext.location.lat(),
29387             longitude: this.gMapContext.location.lng()
29388         };
29389     },
29390     
29391     getAddressName: function() 
29392     {
29393         return this.gMapContext.locationName;
29394     },
29395     
29396     getAddressComponents: function() 
29397     {
29398         return this.gMapContext.addressComponents;
29399     },
29400     
29401     address_component_from_google_geocode: function(address_components) 
29402     {
29403         var result = {};
29404         
29405         for (var i = 0; i < address_components.length; i++) {
29406             var component = address_components[i];
29407             if (component.types.indexOf("postal_code") >= 0) {
29408                 result.postalCode = component.short_name;
29409             } else if (component.types.indexOf("street_number") >= 0) {
29410                 result.streetNumber = component.short_name;
29411             } else if (component.types.indexOf("route") >= 0) {
29412                 result.streetName = component.short_name;
29413             } else if (component.types.indexOf("neighborhood") >= 0) {
29414                 result.city = component.short_name;
29415             } else if (component.types.indexOf("locality") >= 0) {
29416                 result.city = component.short_name;
29417             } else if (component.types.indexOf("sublocality") >= 0) {
29418                 result.district = component.short_name;
29419             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29420                 result.stateOrProvince = component.short_name;
29421             } else if (component.types.indexOf("country") >= 0) {
29422                 result.country = component.short_name;
29423             }
29424         }
29425         
29426         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29427         result.addressLine2 = "";
29428         return result;
29429     },
29430     
29431     setZoomLevel: function(zoom)
29432     {
29433         this.gMapContext.map.setZoom(zoom);
29434     },
29435     
29436     show: function()
29437     {
29438         if(!this.el){
29439             return;
29440         }
29441         
29442         this.el.show();
29443         
29444         this.resize();
29445         
29446         this.fireEvent('show', this);
29447     },
29448     
29449     hide: function()
29450     {
29451         if(!this.el){
29452             return;
29453         }
29454         
29455         this.el.hide();
29456         
29457         this.fireEvent('hide', this);
29458     }
29459     
29460 });
29461
29462 Roo.apply(Roo.bootstrap.LocationPicker, {
29463     
29464     OverlayView : function(map, options)
29465     {
29466         options = options || {};
29467         
29468         this.setMap(map);
29469     }
29470     
29471     
29472 });/**
29473  * @class Roo.bootstrap.Alert
29474  * @extends Roo.bootstrap.Component
29475  * Bootstrap Alert class - shows an alert area box
29476  * eg
29477  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29478   Enter a valid email address
29479 </div>
29480  * @licence LGPL
29481  * @cfg {String} title The title of alert
29482  * @cfg {String} html The content of alert
29483  * @cfg {String} weight (  success | info | warning | danger )
29484  * @cfg {String} faicon font-awesomeicon
29485  * 
29486  * @constructor
29487  * Create a new alert
29488  * @param {Object} config The config object
29489  */
29490
29491
29492 Roo.bootstrap.Alert = function(config){
29493     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29494     
29495 };
29496
29497 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29498     
29499     title: '',
29500     html: '',
29501     weight: false,
29502     faicon: false,
29503     
29504     getAutoCreate : function()
29505     {
29506         
29507         var cfg = {
29508             tag : 'div',
29509             cls : 'alert',
29510             cn : [
29511                 {
29512                     tag : 'i',
29513                     cls : 'roo-alert-icon'
29514                     
29515                 },
29516                 {
29517                     tag : 'b',
29518                     cls : 'roo-alert-title',
29519                     html : this.title
29520                 },
29521                 {
29522                     tag : 'span',
29523                     cls : 'roo-alert-text',
29524                     html : this.html
29525                 }
29526             ]
29527         };
29528         
29529         if(this.faicon){
29530             cfg.cn[0].cls += ' fa ' + this.faicon;
29531         }
29532         
29533         if(this.weight){
29534             cfg.cls += ' alert-' + this.weight;
29535         }
29536         
29537         return cfg;
29538     },
29539     
29540     initEvents: function() 
29541     {
29542         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29543     },
29544     
29545     setTitle : function(str)
29546     {
29547         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29548     },
29549     
29550     setText : function(str)
29551     {
29552         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29553     },
29554     
29555     setWeight : function(weight)
29556     {
29557         if(this.weight){
29558             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29559         }
29560         
29561         this.weight = weight;
29562         
29563         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29564     },
29565     
29566     setIcon : function(icon)
29567     {
29568         if(this.faicon){
29569             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29570         }
29571         
29572         this.faicon = icon;
29573         
29574         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29575     },
29576     
29577     hide: function() 
29578     {
29579         this.el.hide();   
29580     },
29581     
29582     show: function() 
29583     {  
29584         this.el.show();   
29585     }
29586     
29587 });
29588
29589  
29590 /*
29591 * Licence: LGPL
29592 */
29593
29594 /**
29595  * @class Roo.bootstrap.UploadCropbox
29596  * @extends Roo.bootstrap.Component
29597  * Bootstrap UploadCropbox class
29598  * @cfg {String} emptyText show when image has been loaded
29599  * @cfg {String} rotateNotify show when image too small to rotate
29600  * @cfg {Number} errorTimeout default 3000
29601  * @cfg {Number} minWidth default 300
29602  * @cfg {Number} minHeight default 300
29603  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29604  * @cfg {Boolean} isDocument (true|false) default false
29605  * @cfg {String} url action url
29606  * @cfg {String} paramName default 'imageUpload'
29607  * @cfg {String} method default POST
29608  * @cfg {Boolean} loadMask (true|false) default true
29609  * @cfg {Boolean} loadingText default 'Loading...'
29610  * 
29611  * @constructor
29612  * Create a new UploadCropbox
29613  * @param {Object} config The config object
29614  */
29615
29616 Roo.bootstrap.UploadCropbox = function(config){
29617     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29618     
29619     this.addEvents({
29620         /**
29621          * @event beforeselectfile
29622          * Fire before select file
29623          * @param {Roo.bootstrap.UploadCropbox} this
29624          */
29625         "beforeselectfile" : true,
29626         /**
29627          * @event initial
29628          * Fire after initEvent
29629          * @param {Roo.bootstrap.UploadCropbox} this
29630          */
29631         "initial" : true,
29632         /**
29633          * @event crop
29634          * Fire after initEvent
29635          * @param {Roo.bootstrap.UploadCropbox} this
29636          * @param {String} data
29637          */
29638         "crop" : true,
29639         /**
29640          * @event prepare
29641          * Fire when preparing the file data
29642          * @param {Roo.bootstrap.UploadCropbox} this
29643          * @param {Object} file
29644          */
29645         "prepare" : true,
29646         /**
29647          * @event exception
29648          * Fire when get exception
29649          * @param {Roo.bootstrap.UploadCropbox} this
29650          * @param {XMLHttpRequest} xhr
29651          */
29652         "exception" : true,
29653         /**
29654          * @event beforeloadcanvas
29655          * Fire before load the canvas
29656          * @param {Roo.bootstrap.UploadCropbox} this
29657          * @param {String} src
29658          */
29659         "beforeloadcanvas" : true,
29660         /**
29661          * @event trash
29662          * Fire when trash image
29663          * @param {Roo.bootstrap.UploadCropbox} this
29664          */
29665         "trash" : true,
29666         /**
29667          * @event download
29668          * Fire when download the image
29669          * @param {Roo.bootstrap.UploadCropbox} this
29670          */
29671         "download" : true,
29672         /**
29673          * @event footerbuttonclick
29674          * Fire when footerbuttonclick
29675          * @param {Roo.bootstrap.UploadCropbox} this
29676          * @param {String} type
29677          */
29678         "footerbuttonclick" : true,
29679         /**
29680          * @event resize
29681          * Fire when resize
29682          * @param {Roo.bootstrap.UploadCropbox} this
29683          */
29684         "resize" : true,
29685         /**
29686          * @event rotate
29687          * Fire when rotate the image
29688          * @param {Roo.bootstrap.UploadCropbox} this
29689          * @param {String} pos
29690          */
29691         "rotate" : true,
29692         /**
29693          * @event inspect
29694          * Fire when inspect the file
29695          * @param {Roo.bootstrap.UploadCropbox} this
29696          * @param {Object} file
29697          */
29698         "inspect" : true,
29699         /**
29700          * @event upload
29701          * Fire when xhr upload the file
29702          * @param {Roo.bootstrap.UploadCropbox} this
29703          * @param {Object} data
29704          */
29705         "upload" : true,
29706         /**
29707          * @event arrange
29708          * Fire when arrange the file data
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          * @param {Object} formData
29711          */
29712         "arrange" : true
29713     });
29714     
29715     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29716 };
29717
29718 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29719     
29720     emptyText : 'Click to upload image',
29721     rotateNotify : 'Image is too small to rotate',
29722     errorTimeout : 3000,
29723     scale : 0,
29724     baseScale : 1,
29725     rotate : 0,
29726     dragable : false,
29727     pinching : false,
29728     mouseX : 0,
29729     mouseY : 0,
29730     cropData : false,
29731     minWidth : 300,
29732     minHeight : 300,
29733     file : false,
29734     exif : {},
29735     baseRotate : 1,
29736     cropType : 'image/jpeg',
29737     buttons : false,
29738     canvasLoaded : false,
29739     isDocument : false,
29740     method : 'POST',
29741     paramName : 'imageUpload',
29742     loadMask : true,
29743     loadingText : 'Loading...',
29744     maskEl : false,
29745     
29746     getAutoCreate : function()
29747     {
29748         var cfg = {
29749             tag : 'div',
29750             cls : 'roo-upload-cropbox',
29751             cn : [
29752                 {
29753                     tag : 'input',
29754                     cls : 'roo-upload-cropbox-selector',
29755                     type : 'file'
29756                 },
29757                 {
29758                     tag : 'div',
29759                     cls : 'roo-upload-cropbox-body',
29760                     style : 'cursor:pointer',
29761                     cn : [
29762                         {
29763                             tag : 'div',
29764                             cls : 'roo-upload-cropbox-preview'
29765                         },
29766                         {
29767                             tag : 'div',
29768                             cls : 'roo-upload-cropbox-thumb'
29769                         },
29770                         {
29771                             tag : 'div',
29772                             cls : 'roo-upload-cropbox-empty-notify',
29773                             html : this.emptyText
29774                         },
29775                         {
29776                             tag : 'div',
29777                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29778                             html : this.rotateNotify
29779                         }
29780                     ]
29781                 },
29782                 {
29783                     tag : 'div',
29784                     cls : 'roo-upload-cropbox-footer',
29785                     cn : {
29786                         tag : 'div',
29787                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29788                         cn : []
29789                     }
29790                 }
29791             ]
29792         };
29793         
29794         return cfg;
29795     },
29796     
29797     onRender : function(ct, position)
29798     {
29799         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29800         
29801         if (this.buttons.length) {
29802             
29803             Roo.each(this.buttons, function(bb) {
29804                 
29805                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29806                 
29807                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29808                 
29809             }, this);
29810         }
29811         
29812         if(this.loadMask){
29813             this.maskEl = this.el;
29814         }
29815     },
29816     
29817     initEvents : function()
29818     {
29819         this.urlAPI = (window.createObjectURL && window) || 
29820                                 (window.URL && URL.revokeObjectURL && URL) || 
29821                                 (window.webkitURL && webkitURL);
29822                         
29823         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29824         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29825         
29826         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29827         this.selectorEl.hide();
29828         
29829         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29830         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29831         
29832         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29833         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29834         this.thumbEl.hide();
29835         
29836         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29837         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29838         
29839         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29840         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29841         this.errorEl.hide();
29842         
29843         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29844         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29845         this.footerEl.hide();
29846         
29847         this.setThumbBoxSize();
29848         
29849         this.bind();
29850         
29851         this.resize();
29852         
29853         this.fireEvent('initial', this);
29854     },
29855
29856     bind : function()
29857     {
29858         var _this = this;
29859         
29860         window.addEventListener("resize", function() { _this.resize(); } );
29861         
29862         this.bodyEl.on('click', this.beforeSelectFile, this);
29863         
29864         if(Roo.isTouch){
29865             this.bodyEl.on('touchstart', this.onTouchStart, this);
29866             this.bodyEl.on('touchmove', this.onTouchMove, this);
29867             this.bodyEl.on('touchend', this.onTouchEnd, this);
29868         }
29869         
29870         if(!Roo.isTouch){
29871             this.bodyEl.on('mousedown', this.onMouseDown, this);
29872             this.bodyEl.on('mousemove', this.onMouseMove, this);
29873             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29874             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29875             Roo.get(document).on('mouseup', this.onMouseUp, this);
29876         }
29877         
29878         this.selectorEl.on('change', this.onFileSelected, this);
29879     },
29880     
29881     reset : function()
29882     {    
29883         this.scale = 0;
29884         this.baseScale = 1;
29885         this.rotate = 0;
29886         this.baseRotate = 1;
29887         this.dragable = false;
29888         this.pinching = false;
29889         this.mouseX = 0;
29890         this.mouseY = 0;
29891         this.cropData = false;
29892         this.notifyEl.dom.innerHTML = this.emptyText;
29893         
29894         this.selectorEl.dom.value = '';
29895         
29896     },
29897     
29898     resize : function()
29899     {
29900         if(this.fireEvent('resize', this) != false){
29901             this.setThumbBoxPosition();
29902             this.setCanvasPosition();
29903         }
29904     },
29905     
29906     onFooterButtonClick : function(e, el, o, type)
29907     {
29908         switch (type) {
29909             case 'rotate-left' :
29910                 this.onRotateLeft(e);
29911                 break;
29912             case 'rotate-right' :
29913                 this.onRotateRight(e);
29914                 break;
29915             case 'picture' :
29916                 this.beforeSelectFile(e);
29917                 break;
29918             case 'trash' :
29919                 this.trash(e);
29920                 break;
29921             case 'crop' :
29922                 this.crop(e);
29923                 break;
29924             case 'download' :
29925                 this.download(e);
29926                 break;
29927             default :
29928                 break;
29929         }
29930         
29931         this.fireEvent('footerbuttonclick', this, type);
29932     },
29933     
29934     beforeSelectFile : function(e)
29935     {
29936         e.preventDefault();
29937         
29938         if(this.fireEvent('beforeselectfile', this) != false){
29939             this.selectorEl.dom.click();
29940         }
29941     },
29942     
29943     onFileSelected : function(e)
29944     {
29945         e.preventDefault();
29946         
29947         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29948             return;
29949         }
29950         
29951         var file = this.selectorEl.dom.files[0];
29952         
29953         if(this.fireEvent('inspect', this, file) != false){
29954             this.prepare(file);
29955         }
29956         
29957     },
29958     
29959     trash : function(e)
29960     {
29961         this.fireEvent('trash', this);
29962     },
29963     
29964     download : function(e)
29965     {
29966         this.fireEvent('download', this);
29967     },
29968     
29969     loadCanvas : function(src)
29970     {   
29971         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29972             
29973             this.reset();
29974             
29975             this.imageEl = document.createElement('img');
29976             
29977             var _this = this;
29978             
29979             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29980             
29981             this.imageEl.src = src;
29982         }
29983     },
29984     
29985     onLoadCanvas : function()
29986     {   
29987         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29988         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29989         
29990         this.bodyEl.un('click', this.beforeSelectFile, this);
29991         
29992         this.notifyEl.hide();
29993         this.thumbEl.show();
29994         this.footerEl.show();
29995         
29996         this.baseRotateLevel();
29997         
29998         if(this.isDocument){
29999             this.setThumbBoxSize();
30000         }
30001         
30002         this.setThumbBoxPosition();
30003         
30004         this.baseScaleLevel();
30005         
30006         this.draw();
30007         
30008         this.resize();
30009         
30010         this.canvasLoaded = true;
30011         
30012         if(this.loadMask){
30013             this.maskEl.unmask();
30014         }
30015         
30016     },
30017     
30018     setCanvasPosition : function()
30019     {   
30020         if(!this.canvasEl){
30021             return;
30022         }
30023         
30024         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30025         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30026         
30027         this.previewEl.setLeft(pw);
30028         this.previewEl.setTop(ph);
30029         
30030     },
30031     
30032     onMouseDown : function(e)
30033     {   
30034         e.stopEvent();
30035         
30036         this.dragable = true;
30037         this.pinching = false;
30038         
30039         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30040             this.dragable = false;
30041             return;
30042         }
30043         
30044         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30045         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30046         
30047     },
30048     
30049     onMouseMove : function(e)
30050     {   
30051         e.stopEvent();
30052         
30053         if(!this.canvasLoaded){
30054             return;
30055         }
30056         
30057         if (!this.dragable){
30058             return;
30059         }
30060         
30061         var minX = Math.ceil(this.thumbEl.getLeft(true));
30062         var minY = Math.ceil(this.thumbEl.getTop(true));
30063         
30064         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30065         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30066         
30067         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30068         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30069         
30070         x = x - this.mouseX;
30071         y = y - this.mouseY;
30072         
30073         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30074         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30075         
30076         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30077         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30078         
30079         this.previewEl.setLeft(bgX);
30080         this.previewEl.setTop(bgY);
30081         
30082         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30083         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30084     },
30085     
30086     onMouseUp : function(e)
30087     {   
30088         e.stopEvent();
30089         
30090         this.dragable = false;
30091     },
30092     
30093     onMouseWheel : function(e)
30094     {   
30095         e.stopEvent();
30096         
30097         this.startScale = this.scale;
30098         
30099         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30100         
30101         if(!this.zoomable()){
30102             this.scale = this.startScale;
30103             return;
30104         }
30105         
30106         this.draw();
30107         
30108         return;
30109     },
30110     
30111     zoomable : function()
30112     {
30113         var minScale = this.thumbEl.getWidth() / this.minWidth;
30114         
30115         if(this.minWidth < this.minHeight){
30116             minScale = this.thumbEl.getHeight() / this.minHeight;
30117         }
30118         
30119         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30120         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30121         
30122         if(
30123                 this.isDocument &&
30124                 (this.rotate == 0 || this.rotate == 180) && 
30125                 (
30126                     width > this.imageEl.OriginWidth || 
30127                     height > this.imageEl.OriginHeight ||
30128                     (width < this.minWidth && height < this.minHeight)
30129                 )
30130         ){
30131             return false;
30132         }
30133         
30134         if(
30135                 this.isDocument &&
30136                 (this.rotate == 90 || this.rotate == 270) && 
30137                 (
30138                     width > this.imageEl.OriginWidth || 
30139                     height > this.imageEl.OriginHeight ||
30140                     (width < this.minHeight && height < this.minWidth)
30141                 )
30142         ){
30143             return false;
30144         }
30145         
30146         if(
30147                 !this.isDocument &&
30148                 (this.rotate == 0 || this.rotate == 180) && 
30149                 (
30150                     width < this.minWidth || 
30151                     width > this.imageEl.OriginWidth || 
30152                     height < this.minHeight || 
30153                     height > this.imageEl.OriginHeight
30154                 )
30155         ){
30156             return false;
30157         }
30158         
30159         if(
30160                 !this.isDocument &&
30161                 (this.rotate == 90 || this.rotate == 270) && 
30162                 (
30163                     width < this.minHeight || 
30164                     width > this.imageEl.OriginWidth || 
30165                     height < this.minWidth || 
30166                     height > this.imageEl.OriginHeight
30167                 )
30168         ){
30169             return false;
30170         }
30171         
30172         return true;
30173         
30174     },
30175     
30176     onRotateLeft : function(e)
30177     {   
30178         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30179             
30180             var minScale = this.thumbEl.getWidth() / this.minWidth;
30181             
30182             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30183             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30184             
30185             this.startScale = this.scale;
30186             
30187             while (this.getScaleLevel() < minScale){
30188             
30189                 this.scale = this.scale + 1;
30190                 
30191                 if(!this.zoomable()){
30192                     break;
30193                 }
30194                 
30195                 if(
30196                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30197                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30198                 ){
30199                     continue;
30200                 }
30201                 
30202                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30203
30204                 this.draw();
30205                 
30206                 return;
30207             }
30208             
30209             this.scale = this.startScale;
30210             
30211             this.onRotateFail();
30212             
30213             return false;
30214         }
30215         
30216         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30217
30218         if(this.isDocument){
30219             this.setThumbBoxSize();
30220             this.setThumbBoxPosition();
30221             this.setCanvasPosition();
30222         }
30223         
30224         this.draw();
30225         
30226         this.fireEvent('rotate', this, 'left');
30227         
30228     },
30229     
30230     onRotateRight : function(e)
30231     {
30232         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30233             
30234             var minScale = this.thumbEl.getWidth() / this.minWidth;
30235         
30236             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30237             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30238             
30239             this.startScale = this.scale;
30240             
30241             while (this.getScaleLevel() < minScale){
30242             
30243                 this.scale = this.scale + 1;
30244                 
30245                 if(!this.zoomable()){
30246                     break;
30247                 }
30248                 
30249                 if(
30250                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30251                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30252                 ){
30253                     continue;
30254                 }
30255                 
30256                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30257
30258                 this.draw();
30259                 
30260                 return;
30261             }
30262             
30263             this.scale = this.startScale;
30264             
30265             this.onRotateFail();
30266             
30267             return false;
30268         }
30269         
30270         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30271
30272         if(this.isDocument){
30273             this.setThumbBoxSize();
30274             this.setThumbBoxPosition();
30275             this.setCanvasPosition();
30276         }
30277         
30278         this.draw();
30279         
30280         this.fireEvent('rotate', this, 'right');
30281     },
30282     
30283     onRotateFail : function()
30284     {
30285         this.errorEl.show(true);
30286         
30287         var _this = this;
30288         
30289         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30290     },
30291     
30292     draw : function()
30293     {
30294         this.previewEl.dom.innerHTML = '';
30295         
30296         var canvasEl = document.createElement("canvas");
30297         
30298         var contextEl = canvasEl.getContext("2d");
30299         
30300         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30301         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30302         var center = this.imageEl.OriginWidth / 2;
30303         
30304         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30305             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30306             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30307             center = this.imageEl.OriginHeight / 2;
30308         }
30309         
30310         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30311         
30312         contextEl.translate(center, center);
30313         contextEl.rotate(this.rotate * Math.PI / 180);
30314
30315         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30316         
30317         this.canvasEl = document.createElement("canvas");
30318         
30319         this.contextEl = this.canvasEl.getContext("2d");
30320         
30321         switch (this.rotate) {
30322             case 0 :
30323                 
30324                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30325                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30326                 
30327                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30328                 
30329                 break;
30330             case 90 : 
30331                 
30332                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30333                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30334                 
30335                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30336                     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);
30337                     break;
30338                 }
30339                 
30340                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30341                 
30342                 break;
30343             case 180 :
30344                 
30345                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30346                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30347                 
30348                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30349                     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);
30350                     break;
30351                 }
30352                 
30353                 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);
30354                 
30355                 break;
30356             case 270 :
30357                 
30358                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30359                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30360         
30361                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30362                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30363                     break;
30364                 }
30365                 
30366                 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);
30367                 
30368                 break;
30369             default : 
30370                 break;
30371         }
30372         
30373         this.previewEl.appendChild(this.canvasEl);
30374         
30375         this.setCanvasPosition();
30376     },
30377     
30378     crop : function()
30379     {
30380         if(!this.canvasLoaded){
30381             return;
30382         }
30383         
30384         var imageCanvas = document.createElement("canvas");
30385         
30386         var imageContext = imageCanvas.getContext("2d");
30387         
30388         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30389         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30390         
30391         var center = imageCanvas.width / 2;
30392         
30393         imageContext.translate(center, center);
30394         
30395         imageContext.rotate(this.rotate * Math.PI / 180);
30396         
30397         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30398         
30399         var canvas = document.createElement("canvas");
30400         
30401         var context = canvas.getContext("2d");
30402                 
30403         canvas.width = this.minWidth;
30404         canvas.height = this.minHeight;
30405
30406         switch (this.rotate) {
30407             case 0 :
30408                 
30409                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30410                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30411                 
30412                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30413                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30414                 
30415                 var targetWidth = this.minWidth - 2 * x;
30416                 var targetHeight = this.minHeight - 2 * y;
30417                 
30418                 var scale = 1;
30419                 
30420                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30421                     scale = targetWidth / width;
30422                 }
30423                 
30424                 if(x > 0 && y == 0){
30425                     scale = targetHeight / height;
30426                 }
30427                 
30428                 if(x > 0 && y > 0){
30429                     scale = targetWidth / width;
30430                     
30431                     if(width < height){
30432                         scale = targetHeight / height;
30433                     }
30434                 }
30435                 
30436                 context.scale(scale, scale);
30437                 
30438                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30439                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30440
30441                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30442                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30443
30444                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30445                 
30446                 break;
30447             case 90 : 
30448                 
30449                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30450                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30451                 
30452                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30453                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30454                 
30455                 var targetWidth = this.minWidth - 2 * x;
30456                 var targetHeight = this.minHeight - 2 * y;
30457                 
30458                 var scale = 1;
30459                 
30460                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30461                     scale = targetWidth / width;
30462                 }
30463                 
30464                 if(x > 0 && y == 0){
30465                     scale = targetHeight / height;
30466                 }
30467                 
30468                 if(x > 0 && y > 0){
30469                     scale = targetWidth / width;
30470                     
30471                     if(width < height){
30472                         scale = targetHeight / height;
30473                     }
30474                 }
30475                 
30476                 context.scale(scale, scale);
30477                 
30478                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30479                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30480
30481                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30482                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30483                 
30484                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30485                 
30486                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30487                 
30488                 break;
30489             case 180 :
30490                 
30491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30493                 
30494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30496                 
30497                 var targetWidth = this.minWidth - 2 * x;
30498                 var targetHeight = this.minHeight - 2 * y;
30499                 
30500                 var scale = 1;
30501                 
30502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30503                     scale = targetWidth / width;
30504                 }
30505                 
30506                 if(x > 0 && y == 0){
30507                     scale = targetHeight / height;
30508                 }
30509                 
30510                 if(x > 0 && y > 0){
30511                     scale = targetWidth / width;
30512                     
30513                     if(width < height){
30514                         scale = targetHeight / height;
30515                     }
30516                 }
30517                 
30518                 context.scale(scale, scale);
30519                 
30520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30522
30523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30525
30526                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30527                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30528                 
30529                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30530                 
30531                 break;
30532             case 270 :
30533                 
30534                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30535                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30536                 
30537                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30538                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30539                 
30540                 var targetWidth = this.minWidth - 2 * x;
30541                 var targetHeight = this.minHeight - 2 * y;
30542                 
30543                 var scale = 1;
30544                 
30545                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30546                     scale = targetWidth / width;
30547                 }
30548                 
30549                 if(x > 0 && y == 0){
30550                     scale = targetHeight / height;
30551                 }
30552                 
30553                 if(x > 0 && y > 0){
30554                     scale = targetWidth / width;
30555                     
30556                     if(width < height){
30557                         scale = targetHeight / height;
30558                     }
30559                 }
30560                 
30561                 context.scale(scale, scale);
30562                 
30563                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30564                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30565
30566                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30567                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30568                 
30569                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30570                 
30571                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30572                 
30573                 break;
30574             default : 
30575                 break;
30576         }
30577         
30578         this.cropData = canvas.toDataURL(this.cropType);
30579         
30580         if(this.fireEvent('crop', this, this.cropData) !== false){
30581             this.process(this.file, this.cropData);
30582         }
30583         
30584         return;
30585         
30586     },
30587     
30588     setThumbBoxSize : function()
30589     {
30590         var width, height;
30591         
30592         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30593             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30594             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30595             
30596             this.minWidth = width;
30597             this.minHeight = height;
30598             
30599             if(this.rotate == 90 || this.rotate == 270){
30600                 this.minWidth = height;
30601                 this.minHeight = width;
30602             }
30603         }
30604         
30605         height = 300;
30606         width = Math.ceil(this.minWidth * height / this.minHeight);
30607         
30608         if(this.minWidth > this.minHeight){
30609             width = 300;
30610             height = Math.ceil(this.minHeight * width / this.minWidth);
30611         }
30612         
30613         this.thumbEl.setStyle({
30614             width : width + 'px',
30615             height : height + 'px'
30616         });
30617
30618         return;
30619             
30620     },
30621     
30622     setThumbBoxPosition : function()
30623     {
30624         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30625         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30626         
30627         this.thumbEl.setLeft(x);
30628         this.thumbEl.setTop(y);
30629         
30630     },
30631     
30632     baseRotateLevel : function()
30633     {
30634         this.baseRotate = 1;
30635         
30636         if(
30637                 typeof(this.exif) != 'undefined' &&
30638                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30639                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30640         ){
30641             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30642         }
30643         
30644         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30645         
30646     },
30647     
30648     baseScaleLevel : function()
30649     {
30650         var width, height;
30651         
30652         if(this.isDocument){
30653             
30654             if(this.baseRotate == 6 || this.baseRotate == 8){
30655             
30656                 height = this.thumbEl.getHeight();
30657                 this.baseScale = height / this.imageEl.OriginWidth;
30658
30659                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30660                     width = this.thumbEl.getWidth();
30661                     this.baseScale = width / this.imageEl.OriginHeight;
30662                 }
30663
30664                 return;
30665             }
30666
30667             height = this.thumbEl.getHeight();
30668             this.baseScale = height / this.imageEl.OriginHeight;
30669
30670             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30671                 width = this.thumbEl.getWidth();
30672                 this.baseScale = width / this.imageEl.OriginWidth;
30673             }
30674
30675             return;
30676         }
30677         
30678         if(this.baseRotate == 6 || this.baseRotate == 8){
30679             
30680             width = this.thumbEl.getHeight();
30681             this.baseScale = width / this.imageEl.OriginHeight;
30682             
30683             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30684                 height = this.thumbEl.getWidth();
30685                 this.baseScale = height / this.imageEl.OriginHeight;
30686             }
30687             
30688             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30689                 height = this.thumbEl.getWidth();
30690                 this.baseScale = height / this.imageEl.OriginHeight;
30691                 
30692                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30693                     width = this.thumbEl.getHeight();
30694                     this.baseScale = width / this.imageEl.OriginWidth;
30695                 }
30696             }
30697             
30698             return;
30699         }
30700         
30701         width = this.thumbEl.getWidth();
30702         this.baseScale = width / this.imageEl.OriginWidth;
30703         
30704         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30705             height = this.thumbEl.getHeight();
30706             this.baseScale = height / this.imageEl.OriginHeight;
30707         }
30708         
30709         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30710             
30711             height = this.thumbEl.getHeight();
30712             this.baseScale = height / this.imageEl.OriginHeight;
30713             
30714             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30715                 width = this.thumbEl.getWidth();
30716                 this.baseScale = width / this.imageEl.OriginWidth;
30717             }
30718             
30719         }
30720         
30721         return;
30722     },
30723     
30724     getScaleLevel : function()
30725     {
30726         return this.baseScale * Math.pow(1.1, this.scale);
30727     },
30728     
30729     onTouchStart : function(e)
30730     {
30731         if(!this.canvasLoaded){
30732             this.beforeSelectFile(e);
30733             return;
30734         }
30735         
30736         var touches = e.browserEvent.touches;
30737         
30738         if(!touches){
30739             return;
30740         }
30741         
30742         if(touches.length == 1){
30743             this.onMouseDown(e);
30744             return;
30745         }
30746         
30747         if(touches.length != 2){
30748             return;
30749         }
30750         
30751         var coords = [];
30752         
30753         for(var i = 0, finger; finger = touches[i]; i++){
30754             coords.push(finger.pageX, finger.pageY);
30755         }
30756         
30757         var x = Math.pow(coords[0] - coords[2], 2);
30758         var y = Math.pow(coords[1] - coords[3], 2);
30759         
30760         this.startDistance = Math.sqrt(x + y);
30761         
30762         this.startScale = this.scale;
30763         
30764         this.pinching = true;
30765         this.dragable = false;
30766         
30767     },
30768     
30769     onTouchMove : function(e)
30770     {
30771         if(!this.pinching && !this.dragable){
30772             return;
30773         }
30774         
30775         var touches = e.browserEvent.touches;
30776         
30777         if(!touches){
30778             return;
30779         }
30780         
30781         if(this.dragable){
30782             this.onMouseMove(e);
30783             return;
30784         }
30785         
30786         var coords = [];
30787         
30788         for(var i = 0, finger; finger = touches[i]; i++){
30789             coords.push(finger.pageX, finger.pageY);
30790         }
30791         
30792         var x = Math.pow(coords[0] - coords[2], 2);
30793         var y = Math.pow(coords[1] - coords[3], 2);
30794         
30795         this.endDistance = Math.sqrt(x + y);
30796         
30797         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30798         
30799         if(!this.zoomable()){
30800             this.scale = this.startScale;
30801             return;
30802         }
30803         
30804         this.draw();
30805         
30806     },
30807     
30808     onTouchEnd : function(e)
30809     {
30810         this.pinching = false;
30811         this.dragable = false;
30812         
30813     },
30814     
30815     process : function(file, crop)
30816     {
30817         if(this.loadMask){
30818             this.maskEl.mask(this.loadingText);
30819         }
30820         
30821         this.xhr = new XMLHttpRequest();
30822         
30823         file.xhr = this.xhr;
30824
30825         this.xhr.open(this.method, this.url, true);
30826         
30827         var headers = {
30828             "Accept": "application/json",
30829             "Cache-Control": "no-cache",
30830             "X-Requested-With": "XMLHttpRequest"
30831         };
30832         
30833         for (var headerName in headers) {
30834             var headerValue = headers[headerName];
30835             if (headerValue) {
30836                 this.xhr.setRequestHeader(headerName, headerValue);
30837             }
30838         }
30839         
30840         var _this = this;
30841         
30842         this.xhr.onload = function()
30843         {
30844             _this.xhrOnLoad(_this.xhr);
30845         }
30846         
30847         this.xhr.onerror = function()
30848         {
30849             _this.xhrOnError(_this.xhr);
30850         }
30851         
30852         var formData = new FormData();
30853
30854         formData.append('returnHTML', 'NO');
30855         
30856         if(crop){
30857             formData.append('crop', crop);
30858         }
30859         
30860         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30861             formData.append(this.paramName, file, file.name);
30862         }
30863         
30864         if(typeof(file.filename) != 'undefined'){
30865             formData.append('filename', file.filename);
30866         }
30867         
30868         if(typeof(file.mimetype) != 'undefined'){
30869             formData.append('mimetype', file.mimetype);
30870         }
30871         
30872         if(this.fireEvent('arrange', this, formData) != false){
30873             this.xhr.send(formData);
30874         };
30875     },
30876     
30877     xhrOnLoad : function(xhr)
30878     {
30879         if(this.loadMask){
30880             this.maskEl.unmask();
30881         }
30882         
30883         if (xhr.readyState !== 4) {
30884             this.fireEvent('exception', this, xhr);
30885             return;
30886         }
30887
30888         var response = Roo.decode(xhr.responseText);
30889         
30890         if(!response.success){
30891             this.fireEvent('exception', this, xhr);
30892             return;
30893         }
30894         
30895         var response = Roo.decode(xhr.responseText);
30896         
30897         this.fireEvent('upload', this, response);
30898         
30899     },
30900     
30901     xhrOnError : function()
30902     {
30903         if(this.loadMask){
30904             this.maskEl.unmask();
30905         }
30906         
30907         Roo.log('xhr on error');
30908         
30909         var response = Roo.decode(xhr.responseText);
30910           
30911         Roo.log(response);
30912         
30913     },
30914     
30915     prepare : function(file)
30916     {   
30917         if(this.loadMask){
30918             this.maskEl.mask(this.loadingText);
30919         }
30920         
30921         this.file = false;
30922         this.exif = {};
30923         
30924         if(typeof(file) === 'string'){
30925             this.loadCanvas(file);
30926             return;
30927         }
30928         
30929         if(!file || !this.urlAPI){
30930             return;
30931         }
30932         
30933         this.file = file;
30934         this.cropType = file.type;
30935         
30936         var _this = this;
30937         
30938         if(this.fireEvent('prepare', this, this.file) != false){
30939             
30940             var reader = new FileReader();
30941             
30942             reader.onload = function (e) {
30943                 if (e.target.error) {
30944                     Roo.log(e.target.error);
30945                     return;
30946                 }
30947                 
30948                 var buffer = e.target.result,
30949                     dataView = new DataView(buffer),
30950                     offset = 2,
30951                     maxOffset = dataView.byteLength - 4,
30952                     markerBytes,
30953                     markerLength;
30954                 
30955                 if (dataView.getUint16(0) === 0xffd8) {
30956                     while (offset < maxOffset) {
30957                         markerBytes = dataView.getUint16(offset);
30958                         
30959                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30960                             markerLength = dataView.getUint16(offset + 2) + 2;
30961                             if (offset + markerLength > dataView.byteLength) {
30962                                 Roo.log('Invalid meta data: Invalid segment size.');
30963                                 break;
30964                             }
30965                             
30966                             if(markerBytes == 0xffe1){
30967                                 _this.parseExifData(
30968                                     dataView,
30969                                     offset,
30970                                     markerLength
30971                                 );
30972                             }
30973                             
30974                             offset += markerLength;
30975                             
30976                             continue;
30977                         }
30978                         
30979                         break;
30980                     }
30981                     
30982                 }
30983                 
30984                 var url = _this.urlAPI.createObjectURL(_this.file);
30985                 
30986                 _this.loadCanvas(url);
30987                 
30988                 return;
30989             }
30990             
30991             reader.readAsArrayBuffer(this.file);
30992             
30993         }
30994         
30995     },
30996     
30997     parseExifData : function(dataView, offset, length)
30998     {
30999         var tiffOffset = offset + 10,
31000             littleEndian,
31001             dirOffset;
31002     
31003         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31004             // No Exif data, might be XMP data instead
31005             return;
31006         }
31007         
31008         // Check for the ASCII code for "Exif" (0x45786966):
31009         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31010             // No Exif data, might be XMP data instead
31011             return;
31012         }
31013         if (tiffOffset + 8 > dataView.byteLength) {
31014             Roo.log('Invalid Exif data: Invalid segment size.');
31015             return;
31016         }
31017         // Check for the two null bytes:
31018         if (dataView.getUint16(offset + 8) !== 0x0000) {
31019             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31020             return;
31021         }
31022         // Check the byte alignment:
31023         switch (dataView.getUint16(tiffOffset)) {
31024         case 0x4949:
31025             littleEndian = true;
31026             break;
31027         case 0x4D4D:
31028             littleEndian = false;
31029             break;
31030         default:
31031             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31032             return;
31033         }
31034         // Check for the TIFF tag marker (0x002A):
31035         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31036             Roo.log('Invalid Exif data: Missing TIFF marker.');
31037             return;
31038         }
31039         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31040         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31041         
31042         this.parseExifTags(
31043             dataView,
31044             tiffOffset,
31045             tiffOffset + dirOffset,
31046             littleEndian
31047         );
31048     },
31049     
31050     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31051     {
31052         var tagsNumber,
31053             dirEndOffset,
31054             i;
31055         if (dirOffset + 6 > dataView.byteLength) {
31056             Roo.log('Invalid Exif data: Invalid directory offset.');
31057             return;
31058         }
31059         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31060         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31061         if (dirEndOffset + 4 > dataView.byteLength) {
31062             Roo.log('Invalid Exif data: Invalid directory size.');
31063             return;
31064         }
31065         for (i = 0; i < tagsNumber; i += 1) {
31066             this.parseExifTag(
31067                 dataView,
31068                 tiffOffset,
31069                 dirOffset + 2 + 12 * i, // tag offset
31070                 littleEndian
31071             );
31072         }
31073         // Return the offset to the next directory:
31074         return dataView.getUint32(dirEndOffset, littleEndian);
31075     },
31076     
31077     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31078     {
31079         var tag = dataView.getUint16(offset, littleEndian);
31080         
31081         this.exif[tag] = this.getExifValue(
31082             dataView,
31083             tiffOffset,
31084             offset,
31085             dataView.getUint16(offset + 2, littleEndian), // tag type
31086             dataView.getUint32(offset + 4, littleEndian), // tag length
31087             littleEndian
31088         );
31089     },
31090     
31091     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31092     {
31093         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31094             tagSize,
31095             dataOffset,
31096             values,
31097             i,
31098             str,
31099             c;
31100     
31101         if (!tagType) {
31102             Roo.log('Invalid Exif data: Invalid tag type.');
31103             return;
31104         }
31105         
31106         tagSize = tagType.size * length;
31107         // Determine if the value is contained in the dataOffset bytes,
31108         // or if the value at the dataOffset is a pointer to the actual data:
31109         dataOffset = tagSize > 4 ?
31110                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31111         if (dataOffset + tagSize > dataView.byteLength) {
31112             Roo.log('Invalid Exif data: Invalid data offset.');
31113             return;
31114         }
31115         if (length === 1) {
31116             return tagType.getValue(dataView, dataOffset, littleEndian);
31117         }
31118         values = [];
31119         for (i = 0; i < length; i += 1) {
31120             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31121         }
31122         
31123         if (tagType.ascii) {
31124             str = '';
31125             // Concatenate the chars:
31126             for (i = 0; i < values.length; i += 1) {
31127                 c = values[i];
31128                 // Ignore the terminating NULL byte(s):
31129                 if (c === '\u0000') {
31130                     break;
31131                 }
31132                 str += c;
31133             }
31134             return str;
31135         }
31136         return values;
31137     }
31138     
31139 });
31140
31141 Roo.apply(Roo.bootstrap.UploadCropbox, {
31142     tags : {
31143         'Orientation': 0x0112
31144     },
31145     
31146     Orientation: {
31147             1: 0, //'top-left',
31148 //            2: 'top-right',
31149             3: 180, //'bottom-right',
31150 //            4: 'bottom-left',
31151 //            5: 'left-top',
31152             6: 90, //'right-top',
31153 //            7: 'right-bottom',
31154             8: 270 //'left-bottom'
31155     },
31156     
31157     exifTagTypes : {
31158         // byte, 8-bit unsigned int:
31159         1: {
31160             getValue: function (dataView, dataOffset) {
31161                 return dataView.getUint8(dataOffset);
31162             },
31163             size: 1
31164         },
31165         // ascii, 8-bit byte:
31166         2: {
31167             getValue: function (dataView, dataOffset) {
31168                 return String.fromCharCode(dataView.getUint8(dataOffset));
31169             },
31170             size: 1,
31171             ascii: true
31172         },
31173         // short, 16 bit int:
31174         3: {
31175             getValue: function (dataView, dataOffset, littleEndian) {
31176                 return dataView.getUint16(dataOffset, littleEndian);
31177             },
31178             size: 2
31179         },
31180         // long, 32 bit int:
31181         4: {
31182             getValue: function (dataView, dataOffset, littleEndian) {
31183                 return dataView.getUint32(dataOffset, littleEndian);
31184             },
31185             size: 4
31186         },
31187         // rational = two long values, first is numerator, second is denominator:
31188         5: {
31189             getValue: function (dataView, dataOffset, littleEndian) {
31190                 return dataView.getUint32(dataOffset, littleEndian) /
31191                     dataView.getUint32(dataOffset + 4, littleEndian);
31192             },
31193             size: 8
31194         },
31195         // slong, 32 bit signed int:
31196         9: {
31197             getValue: function (dataView, dataOffset, littleEndian) {
31198                 return dataView.getInt32(dataOffset, littleEndian);
31199             },
31200             size: 4
31201         },
31202         // srational, two slongs, first is numerator, second is denominator:
31203         10: {
31204             getValue: function (dataView, dataOffset, littleEndian) {
31205                 return dataView.getInt32(dataOffset, littleEndian) /
31206                     dataView.getInt32(dataOffset + 4, littleEndian);
31207             },
31208             size: 8
31209         }
31210     },
31211     
31212     footer : {
31213         STANDARD : [
31214             {
31215                 tag : 'div',
31216                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31217                 action : 'rotate-left',
31218                 cn : [
31219                     {
31220                         tag : 'button',
31221                         cls : 'btn btn-default',
31222                         html : '<i class="fa fa-undo"></i>'
31223                     }
31224                 ]
31225             },
31226             {
31227                 tag : 'div',
31228                 cls : 'btn-group roo-upload-cropbox-picture',
31229                 action : 'picture',
31230                 cn : [
31231                     {
31232                         tag : 'button',
31233                         cls : 'btn btn-default',
31234                         html : '<i class="fa fa-picture-o"></i>'
31235                     }
31236                 ]
31237             },
31238             {
31239                 tag : 'div',
31240                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31241                 action : 'rotate-right',
31242                 cn : [
31243                     {
31244                         tag : 'button',
31245                         cls : 'btn btn-default',
31246                         html : '<i class="fa fa-repeat"></i>'
31247                     }
31248                 ]
31249             }
31250         ],
31251         DOCUMENT : [
31252             {
31253                 tag : 'div',
31254                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31255                 action : 'rotate-left',
31256                 cn : [
31257                     {
31258                         tag : 'button',
31259                         cls : 'btn btn-default',
31260                         html : '<i class="fa fa-undo"></i>'
31261                     }
31262                 ]
31263             },
31264             {
31265                 tag : 'div',
31266                 cls : 'btn-group roo-upload-cropbox-download',
31267                 action : 'download',
31268                 cn : [
31269                     {
31270                         tag : 'button',
31271                         cls : 'btn btn-default',
31272                         html : '<i class="fa fa-download"></i>'
31273                     }
31274                 ]
31275             },
31276             {
31277                 tag : 'div',
31278                 cls : 'btn-group roo-upload-cropbox-crop',
31279                 action : 'crop',
31280                 cn : [
31281                     {
31282                         tag : 'button',
31283                         cls : 'btn btn-default',
31284                         html : '<i class="fa fa-crop"></i>'
31285                     }
31286                 ]
31287             },
31288             {
31289                 tag : 'div',
31290                 cls : 'btn-group roo-upload-cropbox-trash',
31291                 action : 'trash',
31292                 cn : [
31293                     {
31294                         tag : 'button',
31295                         cls : 'btn btn-default',
31296                         html : '<i class="fa fa-trash"></i>'
31297                     }
31298                 ]
31299             },
31300             {
31301                 tag : 'div',
31302                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31303                 action : 'rotate-right',
31304                 cn : [
31305                     {
31306                         tag : 'button',
31307                         cls : 'btn btn-default',
31308                         html : '<i class="fa fa-repeat"></i>'
31309                     }
31310                 ]
31311             }
31312         ],
31313         ROTATOR : [
31314             {
31315                 tag : 'div',
31316                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31317                 action : 'rotate-left',
31318                 cn : [
31319                     {
31320                         tag : 'button',
31321                         cls : 'btn btn-default',
31322                         html : '<i class="fa fa-undo"></i>'
31323                     }
31324                 ]
31325             },
31326             {
31327                 tag : 'div',
31328                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31329                 action : 'rotate-right',
31330                 cn : [
31331                     {
31332                         tag : 'button',
31333                         cls : 'btn btn-default',
31334                         html : '<i class="fa fa-repeat"></i>'
31335                     }
31336                 ]
31337             }
31338         ]
31339     }
31340 });
31341
31342 /*
31343 * Licence: LGPL
31344 */
31345
31346 /**
31347  * @class Roo.bootstrap.DocumentManager
31348  * @extends Roo.bootstrap.Component
31349  * Bootstrap DocumentManager class
31350  * @cfg {String} paramName default 'imageUpload'
31351  * @cfg {String} toolTipName default 'filename'
31352  * @cfg {String} method default POST
31353  * @cfg {String} url action url
31354  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31355  * @cfg {Boolean} multiple multiple upload default true
31356  * @cfg {Number} thumbSize default 300
31357  * @cfg {String} fieldLabel
31358  * @cfg {Number} labelWidth default 4
31359  * @cfg {String} labelAlign (left|top) default left
31360  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31361 * @cfg {Number} labellg set the width of label (1-12)
31362  * @cfg {Number} labelmd set the width of label (1-12)
31363  * @cfg {Number} labelsm set the width of label (1-12)
31364  * @cfg {Number} labelxs set the width of label (1-12)
31365  * 
31366  * @constructor
31367  * Create a new DocumentManager
31368  * @param {Object} config The config object
31369  */
31370
31371 Roo.bootstrap.DocumentManager = function(config){
31372     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31373     
31374     this.files = [];
31375     this.delegates = [];
31376     
31377     this.addEvents({
31378         /**
31379          * @event initial
31380          * Fire when initial the DocumentManager
31381          * @param {Roo.bootstrap.DocumentManager} this
31382          */
31383         "initial" : true,
31384         /**
31385          * @event inspect
31386          * inspect selected file
31387          * @param {Roo.bootstrap.DocumentManager} this
31388          * @param {File} file
31389          */
31390         "inspect" : true,
31391         /**
31392          * @event exception
31393          * Fire when xhr load exception
31394          * @param {Roo.bootstrap.DocumentManager} this
31395          * @param {XMLHttpRequest} xhr
31396          */
31397         "exception" : true,
31398         /**
31399          * @event afterupload
31400          * Fire when xhr load exception
31401          * @param {Roo.bootstrap.DocumentManager} this
31402          * @param {XMLHttpRequest} xhr
31403          */
31404         "afterupload" : true,
31405         /**
31406          * @event prepare
31407          * prepare the form data
31408          * @param {Roo.bootstrap.DocumentManager} this
31409          * @param {Object} formData
31410          */
31411         "prepare" : true,
31412         /**
31413          * @event remove
31414          * Fire when remove the file
31415          * @param {Roo.bootstrap.DocumentManager} this
31416          * @param {Object} file
31417          */
31418         "remove" : true,
31419         /**
31420          * @event refresh
31421          * Fire after refresh the file
31422          * @param {Roo.bootstrap.DocumentManager} this
31423          */
31424         "refresh" : true,
31425         /**
31426          * @event click
31427          * Fire after click the image
31428          * @param {Roo.bootstrap.DocumentManager} this
31429          * @param {Object} file
31430          */
31431         "click" : true,
31432         /**
31433          * @event edit
31434          * Fire when upload a image and editable set to true
31435          * @param {Roo.bootstrap.DocumentManager} this
31436          * @param {Object} file
31437          */
31438         "edit" : true,
31439         /**
31440          * @event beforeselectfile
31441          * Fire before select file
31442          * @param {Roo.bootstrap.DocumentManager} this
31443          */
31444         "beforeselectfile" : true,
31445         /**
31446          * @event process
31447          * Fire before process file
31448          * @param {Roo.bootstrap.DocumentManager} this
31449          * @param {Object} file
31450          */
31451         "process" : true,
31452         /**
31453          * @event previewrendered
31454          * Fire when preview rendered
31455          * @param {Roo.bootstrap.DocumentManager} this
31456          * @param {Object} file
31457          */
31458         "previewrendered" : true,
31459         /**
31460          */
31461         "previewResize" : true
31462         
31463     });
31464 };
31465
31466 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31467     
31468     boxes : 0,
31469     inputName : '',
31470     thumbSize : 300,
31471     multiple : true,
31472     files : false,
31473     method : 'POST',
31474     url : '',
31475     paramName : 'imageUpload',
31476     toolTipName : 'filename',
31477     fieldLabel : '',
31478     labelWidth : 4,
31479     labelAlign : 'left',
31480     editable : true,
31481     delegates : false,
31482     xhr : false, 
31483     
31484     labellg : 0,
31485     labelmd : 0,
31486     labelsm : 0,
31487     labelxs : 0,
31488     
31489     getAutoCreate : function()
31490     {   
31491         var managerWidget = {
31492             tag : 'div',
31493             cls : 'roo-document-manager',
31494             cn : [
31495                 {
31496                     tag : 'input',
31497                     cls : 'roo-document-manager-selector',
31498                     type : 'file'
31499                 },
31500                 {
31501                     tag : 'div',
31502                     cls : 'roo-document-manager-uploader',
31503                     cn : [
31504                         {
31505                             tag : 'div',
31506                             cls : 'roo-document-manager-upload-btn',
31507                             html : '<i class="fa fa-plus"></i>'
31508                         }
31509                     ]
31510                     
31511                 }
31512             ]
31513         };
31514         
31515         var content = [
31516             {
31517                 tag : 'div',
31518                 cls : 'column col-md-12',
31519                 cn : managerWidget
31520             }
31521         ];
31522         
31523         if(this.fieldLabel.length){
31524             
31525             content = [
31526                 {
31527                     tag : 'div',
31528                     cls : 'column col-md-12',
31529                     html : this.fieldLabel
31530                 },
31531                 {
31532                     tag : 'div',
31533                     cls : 'column col-md-12',
31534                     cn : managerWidget
31535                 }
31536             ];
31537
31538             if(this.labelAlign == 'left'){
31539                 content = [
31540                     {
31541                         tag : 'div',
31542                         cls : 'column',
31543                         html : this.fieldLabel
31544                     },
31545                     {
31546                         tag : 'div',
31547                         cls : 'column',
31548                         cn : managerWidget
31549                     }
31550                 ];
31551                 
31552                 if(this.labelWidth > 12){
31553                     content[0].style = "width: " + this.labelWidth + 'px';
31554                 }
31555
31556                 if(this.labelWidth < 13 && this.labelmd == 0){
31557                     this.labelmd = this.labelWidth;
31558                 }
31559
31560                 if(this.labellg > 0){
31561                     content[0].cls += ' col-lg-' + this.labellg;
31562                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31563                 }
31564
31565                 if(this.labelmd > 0){
31566                     content[0].cls += ' col-md-' + this.labelmd;
31567                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31568                 }
31569
31570                 if(this.labelsm > 0){
31571                     content[0].cls += ' col-sm-' + this.labelsm;
31572                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31573                 }
31574
31575                 if(this.labelxs > 0){
31576                     content[0].cls += ' col-xs-' + this.labelxs;
31577                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31578                 }
31579                 
31580             }
31581         }
31582         
31583         var cfg = {
31584             tag : 'div',
31585             cls : 'row clearfix',
31586             cn : content
31587         };
31588         
31589         return cfg;
31590         
31591     },
31592     
31593     initEvents : function()
31594     {
31595         this.managerEl = this.el.select('.roo-document-manager', true).first();
31596         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31597         
31598         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31599         this.selectorEl.hide();
31600         
31601         if(this.multiple){
31602             this.selectorEl.attr('multiple', 'multiple');
31603         }
31604         
31605         this.selectorEl.on('change', this.onFileSelected, this);
31606         
31607         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31608         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31609         
31610         this.uploader.on('click', this.onUploaderClick, this);
31611         
31612         this.renderProgressDialog();
31613         
31614         var _this = this;
31615         
31616         window.addEventListener("resize", function() { _this.refresh(); } );
31617         
31618         this.fireEvent('initial', this);
31619     },
31620     
31621     renderProgressDialog : function()
31622     {
31623         var _this = this;
31624         
31625         this.progressDialog = new Roo.bootstrap.Modal({
31626             cls : 'roo-document-manager-progress-dialog',
31627             allow_close : false,
31628             animate : false,
31629             title : '',
31630             buttons : [
31631                 {
31632                     name  :'cancel',
31633                     weight : 'danger',
31634                     html : 'Cancel'
31635                 }
31636             ], 
31637             listeners : { 
31638                 btnclick : function() {
31639                     _this.uploadCancel();
31640                     this.hide();
31641                 }
31642             }
31643         });
31644          
31645         this.progressDialog.render(Roo.get(document.body));
31646          
31647         this.progress = new Roo.bootstrap.Progress({
31648             cls : 'roo-document-manager-progress',
31649             active : true,
31650             striped : true
31651         });
31652         
31653         this.progress.render(this.progressDialog.getChildContainer());
31654         
31655         this.progressBar = new Roo.bootstrap.ProgressBar({
31656             cls : 'roo-document-manager-progress-bar',
31657             aria_valuenow : 0,
31658             aria_valuemin : 0,
31659             aria_valuemax : 12,
31660             panel : 'success'
31661         });
31662         
31663         this.progressBar.render(this.progress.getChildContainer());
31664     },
31665     
31666     onUploaderClick : function(e)
31667     {
31668         e.preventDefault();
31669      
31670         if(this.fireEvent('beforeselectfile', this) != false){
31671             this.selectorEl.dom.click();
31672         }
31673         
31674     },
31675     
31676     onFileSelected : function(e)
31677     {
31678         e.preventDefault();
31679         
31680         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31681             return;
31682         }
31683         
31684         Roo.each(this.selectorEl.dom.files, function(file){
31685             if(this.fireEvent('inspect', this, file) != false){
31686                 this.files.push(file);
31687             }
31688         }, this);
31689         
31690         this.queue();
31691         
31692     },
31693     
31694     queue : function()
31695     {
31696         this.selectorEl.dom.value = '';
31697         
31698         if(!this.files || !this.files.length){
31699             return;
31700         }
31701         
31702         if(this.boxes > 0 && this.files.length > this.boxes){
31703             this.files = this.files.slice(0, this.boxes);
31704         }
31705         
31706         this.uploader.show();
31707         
31708         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31709             this.uploader.hide();
31710         }
31711         
31712         var _this = this;
31713         
31714         var files = [];
31715         
31716         var docs = [];
31717         
31718         Roo.each(this.files, function(file){
31719             
31720             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31721                 var f = this.renderPreview(file);
31722                 files.push(f);
31723                 return;
31724             }
31725             
31726             if(file.type.indexOf('image') != -1){
31727                 this.delegates.push(
31728                     (function(){
31729                         _this.process(file);
31730                     }).createDelegate(this)
31731                 );
31732         
31733                 return;
31734             }
31735             
31736             docs.push(
31737                 (function(){
31738                     _this.process(file);
31739                 }).createDelegate(this)
31740             );
31741             
31742         }, this);
31743         
31744         this.files = files;
31745         
31746         this.delegates = this.delegates.concat(docs);
31747         
31748         if(!this.delegates.length){
31749             this.refresh();
31750             return;
31751         }
31752         
31753         this.progressBar.aria_valuemax = this.delegates.length;
31754         
31755         this.arrange();
31756         
31757         return;
31758     },
31759     
31760     arrange : function()
31761     {
31762         if(!this.delegates.length){
31763             this.progressDialog.hide();
31764             this.refresh();
31765             return;
31766         }
31767         
31768         var delegate = this.delegates.shift();
31769         
31770         this.progressDialog.show();
31771         
31772         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31773         
31774         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31775         
31776         delegate();
31777     },
31778     
31779     refresh : function()
31780     {
31781         this.uploader.show();
31782         
31783         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31784             this.uploader.hide();
31785         }
31786         
31787         Roo.isTouch ? this.closable(false) : this.closable(true);
31788         
31789         this.fireEvent('refresh', this);
31790     },
31791     
31792     onRemove : function(e, el, o)
31793     {
31794         e.preventDefault();
31795         
31796         this.fireEvent('remove', this, o);
31797         
31798     },
31799     
31800     remove : function(o)
31801     {
31802         var files = [];
31803         
31804         Roo.each(this.files, function(file){
31805             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31806                 files.push(file);
31807                 return;
31808             }
31809
31810             o.target.remove();
31811
31812         }, this);
31813         
31814         this.files = files;
31815         
31816         this.refresh();
31817     },
31818     
31819     clear : function()
31820     {
31821         Roo.each(this.files, function(file){
31822             if(!file.target){
31823                 return;
31824             }
31825             
31826             file.target.remove();
31827
31828         }, this);
31829         
31830         this.files = [];
31831         
31832         this.refresh();
31833     },
31834     
31835     onClick : function(e, el, o)
31836     {
31837         e.preventDefault();
31838         
31839         this.fireEvent('click', this, o);
31840         
31841     },
31842     
31843     closable : function(closable)
31844     {
31845         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31846             
31847             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31848             
31849             if(closable){
31850                 el.show();
31851                 return;
31852             }
31853             
31854             el.hide();
31855             
31856         }, this);
31857     },
31858     
31859     xhrOnLoad : function(xhr)
31860     {
31861         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31862             el.remove();
31863         }, this);
31864         
31865         if (xhr.readyState !== 4) {
31866             this.arrange();
31867             this.fireEvent('exception', this, xhr);
31868             return;
31869         }
31870
31871         var response = Roo.decode(xhr.responseText);
31872         
31873         if(!response.success){
31874             this.arrange();
31875             this.fireEvent('exception', this, xhr);
31876             return;
31877         }
31878         
31879         var file = this.renderPreview(response.data);
31880         
31881         this.files.push(file);
31882         
31883         this.arrange();
31884         
31885         this.fireEvent('afterupload', this, xhr);
31886         
31887     },
31888     
31889     xhrOnError : function(xhr)
31890     {
31891         Roo.log('xhr on error');
31892         
31893         var response = Roo.decode(xhr.responseText);
31894           
31895         Roo.log(response);
31896         
31897         this.arrange();
31898     },
31899     
31900     process : function(file)
31901     {
31902         if(this.fireEvent('process', this, file) !== false){
31903             if(this.editable && file.type.indexOf('image') != -1){
31904                 this.fireEvent('edit', this, file);
31905                 return;
31906             }
31907
31908             this.uploadStart(file, false);
31909
31910             return;
31911         }
31912         
31913     },
31914     
31915     uploadStart : function(file, crop)
31916     {
31917         this.xhr = new XMLHttpRequest();
31918         
31919         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31920             this.arrange();
31921             return;
31922         }
31923         
31924         file.xhr = this.xhr;
31925             
31926         this.managerEl.createChild({
31927             tag : 'div',
31928             cls : 'roo-document-manager-loading',
31929             cn : [
31930                 {
31931                     tag : 'div',
31932                     tooltip : file.name,
31933                     cls : 'roo-document-manager-thumb',
31934                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31935                 }
31936             ]
31937
31938         });
31939
31940         this.xhr.open(this.method, this.url, true);
31941         
31942         var headers = {
31943             "Accept": "application/json",
31944             "Cache-Control": "no-cache",
31945             "X-Requested-With": "XMLHttpRequest"
31946         };
31947         
31948         for (var headerName in headers) {
31949             var headerValue = headers[headerName];
31950             if (headerValue) {
31951                 this.xhr.setRequestHeader(headerName, headerValue);
31952             }
31953         }
31954         
31955         var _this = this;
31956         
31957         this.xhr.onload = function()
31958         {
31959             _this.xhrOnLoad(_this.xhr);
31960         }
31961         
31962         this.xhr.onerror = function()
31963         {
31964             _this.xhrOnError(_this.xhr);
31965         }
31966         
31967         var formData = new FormData();
31968
31969         formData.append('returnHTML', 'NO');
31970         
31971         if(crop){
31972             formData.append('crop', crop);
31973         }
31974         
31975         formData.append(this.paramName, file, file.name);
31976         
31977         var options = {
31978             file : file, 
31979             manually : false
31980         };
31981         
31982         if(this.fireEvent('prepare', this, formData, options) != false){
31983             
31984             if(options.manually){
31985                 return;
31986             }
31987             
31988             this.xhr.send(formData);
31989             return;
31990         };
31991         
31992         this.uploadCancel();
31993     },
31994     
31995     uploadCancel : function()
31996     {
31997         if (this.xhr) {
31998             this.xhr.abort();
31999         }
32000         
32001         this.delegates = [];
32002         
32003         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32004             el.remove();
32005         }, this);
32006         
32007         this.arrange();
32008     },
32009     
32010     renderPreview : function(file)
32011     {
32012         if(typeof(file.target) != 'undefined' && file.target){
32013             return file;
32014         }
32015         
32016         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32017         
32018         var previewEl = this.managerEl.createChild({
32019             tag : 'div',
32020             cls : 'roo-document-manager-preview',
32021             cn : [
32022                 {
32023                     tag : 'div',
32024                     tooltip : file[this.toolTipName],
32025                     cls : 'roo-document-manager-thumb',
32026                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32027                 },
32028                 {
32029                     tag : 'button',
32030                     cls : 'close',
32031                     html : '<i class="fa fa-times-circle"></i>'
32032                 }
32033             ]
32034         });
32035
32036         var close = previewEl.select('button.close', true).first();
32037
32038         close.on('click', this.onRemove, this, file);
32039
32040         file.target = previewEl;
32041
32042         var image = previewEl.select('img', true).first();
32043         
32044         var _this = this;
32045         
32046         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32047         
32048         image.on('click', this.onClick, this, file);
32049         
32050         this.fireEvent('previewrendered', this, file);
32051         
32052         return file;
32053         
32054     },
32055     
32056     onPreviewLoad : function(file, image)
32057     {
32058         if(typeof(file.target) == 'undefined' || !file.target){
32059             return;
32060         }
32061         
32062         var width = image.dom.naturalWidth || image.dom.width;
32063         var height = image.dom.naturalHeight || image.dom.height;
32064         
32065         if(!this.previewResize) {
32066             return;
32067         }
32068         
32069         if(width > height){
32070             file.target.addClass('wide');
32071             return;
32072         }
32073         
32074         file.target.addClass('tall');
32075         return;
32076         
32077     },
32078     
32079     uploadFromSource : function(file, crop)
32080     {
32081         this.xhr = new XMLHttpRequest();
32082         
32083         this.managerEl.createChild({
32084             tag : 'div',
32085             cls : 'roo-document-manager-loading',
32086             cn : [
32087                 {
32088                     tag : 'div',
32089                     tooltip : file.name,
32090                     cls : 'roo-document-manager-thumb',
32091                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32092                 }
32093             ]
32094
32095         });
32096
32097         this.xhr.open(this.method, this.url, true);
32098         
32099         var headers = {
32100             "Accept": "application/json",
32101             "Cache-Control": "no-cache",
32102             "X-Requested-With": "XMLHttpRequest"
32103         };
32104         
32105         for (var headerName in headers) {
32106             var headerValue = headers[headerName];
32107             if (headerValue) {
32108                 this.xhr.setRequestHeader(headerName, headerValue);
32109             }
32110         }
32111         
32112         var _this = this;
32113         
32114         this.xhr.onload = function()
32115         {
32116             _this.xhrOnLoad(_this.xhr);
32117         }
32118         
32119         this.xhr.onerror = function()
32120         {
32121             _this.xhrOnError(_this.xhr);
32122         }
32123         
32124         var formData = new FormData();
32125
32126         formData.append('returnHTML', 'NO');
32127         
32128         formData.append('crop', crop);
32129         
32130         if(typeof(file.filename) != 'undefined'){
32131             formData.append('filename', file.filename);
32132         }
32133         
32134         if(typeof(file.mimetype) != 'undefined'){
32135             formData.append('mimetype', file.mimetype);
32136         }
32137         
32138         Roo.log(formData);
32139         
32140         if(this.fireEvent('prepare', this, formData) != false){
32141             this.xhr.send(formData);
32142         };
32143     }
32144 });
32145
32146 /*
32147 * Licence: LGPL
32148 */
32149
32150 /**
32151  * @class Roo.bootstrap.DocumentViewer
32152  * @extends Roo.bootstrap.Component
32153  * Bootstrap DocumentViewer class
32154  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32155  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32156  * 
32157  * @constructor
32158  * Create a new DocumentViewer
32159  * @param {Object} config The config object
32160  */
32161
32162 Roo.bootstrap.DocumentViewer = function(config){
32163     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32164     
32165     this.addEvents({
32166         /**
32167          * @event initial
32168          * Fire after initEvent
32169          * @param {Roo.bootstrap.DocumentViewer} this
32170          */
32171         "initial" : true,
32172         /**
32173          * @event click
32174          * Fire after click
32175          * @param {Roo.bootstrap.DocumentViewer} this
32176          */
32177         "click" : true,
32178         /**
32179          * @event download
32180          * Fire after download button
32181          * @param {Roo.bootstrap.DocumentViewer} this
32182          */
32183         "download" : true,
32184         /**
32185          * @event trash
32186          * Fire after trash button
32187          * @param {Roo.bootstrap.DocumentViewer} this
32188          */
32189         "trash" : true
32190         
32191     });
32192 };
32193
32194 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32195     
32196     showDownload : true,
32197     
32198     showTrash : true,
32199     
32200     getAutoCreate : function()
32201     {
32202         var cfg = {
32203             tag : 'div',
32204             cls : 'roo-document-viewer',
32205             cn : [
32206                 {
32207                     tag : 'div',
32208                     cls : 'roo-document-viewer-body',
32209                     cn : [
32210                         {
32211                             tag : 'div',
32212                             cls : 'roo-document-viewer-thumb',
32213                             cn : [
32214                                 {
32215                                     tag : 'img',
32216                                     cls : 'roo-document-viewer-image'
32217                                 }
32218                             ]
32219                         }
32220                     ]
32221                 },
32222                 {
32223                     tag : 'div',
32224                     cls : 'roo-document-viewer-footer',
32225                     cn : {
32226                         tag : 'div',
32227                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32228                         cn : [
32229                             {
32230                                 tag : 'div',
32231                                 cls : 'btn-group roo-document-viewer-download',
32232                                 cn : [
32233                                     {
32234                                         tag : 'button',
32235                                         cls : 'btn btn-default',
32236                                         html : '<i class="fa fa-download"></i>'
32237                                     }
32238                                 ]
32239                             },
32240                             {
32241                                 tag : 'div',
32242                                 cls : 'btn-group roo-document-viewer-trash',
32243                                 cn : [
32244                                     {
32245                                         tag : 'button',
32246                                         cls : 'btn btn-default',
32247                                         html : '<i class="fa fa-trash"></i>'
32248                                     }
32249                                 ]
32250                             }
32251                         ]
32252                     }
32253                 }
32254             ]
32255         };
32256         
32257         return cfg;
32258     },
32259     
32260     initEvents : function()
32261     {
32262         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32263         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32264         
32265         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32266         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32267         
32268         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32269         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32270         
32271         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32272         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32273         
32274         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32275         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32276         
32277         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32278         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32279         
32280         this.bodyEl.on('click', this.onClick, this);
32281         this.downloadBtn.on('click', this.onDownload, this);
32282         this.trashBtn.on('click', this.onTrash, this);
32283         
32284         this.downloadBtn.hide();
32285         this.trashBtn.hide();
32286         
32287         if(this.showDownload){
32288             this.downloadBtn.show();
32289         }
32290         
32291         if(this.showTrash){
32292             this.trashBtn.show();
32293         }
32294         
32295         if(!this.showDownload && !this.showTrash) {
32296             this.footerEl.hide();
32297         }
32298         
32299     },
32300     
32301     initial : function()
32302     {
32303         this.fireEvent('initial', this);
32304         
32305     },
32306     
32307     onClick : function(e)
32308     {
32309         e.preventDefault();
32310         
32311         this.fireEvent('click', this);
32312     },
32313     
32314     onDownload : function(e)
32315     {
32316         e.preventDefault();
32317         
32318         this.fireEvent('download', this);
32319     },
32320     
32321     onTrash : function(e)
32322     {
32323         e.preventDefault();
32324         
32325         this.fireEvent('trash', this);
32326     }
32327     
32328 });
32329 /*
32330  * - LGPL
32331  *
32332  * nav progress bar
32333  * 
32334  */
32335
32336 /**
32337  * @class Roo.bootstrap.NavProgressBar
32338  * @extends Roo.bootstrap.Component
32339  * Bootstrap NavProgressBar class
32340  * 
32341  * @constructor
32342  * Create a new nav progress bar
32343  * @param {Object} config The config object
32344  */
32345
32346 Roo.bootstrap.NavProgressBar = function(config){
32347     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32348
32349     this.bullets = this.bullets || [];
32350    
32351 //    Roo.bootstrap.NavProgressBar.register(this);
32352      this.addEvents({
32353         /**
32354              * @event changed
32355              * Fires when the active item changes
32356              * @param {Roo.bootstrap.NavProgressBar} this
32357              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32358              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32359          */
32360         'changed': true
32361      });
32362     
32363 };
32364
32365 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32366     
32367     bullets : [],
32368     barItems : [],
32369     
32370     getAutoCreate : function()
32371     {
32372         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32373         
32374         cfg = {
32375             tag : 'div',
32376             cls : 'roo-navigation-bar-group',
32377             cn : [
32378                 {
32379                     tag : 'div',
32380                     cls : 'roo-navigation-top-bar'
32381                 },
32382                 {
32383                     tag : 'div',
32384                     cls : 'roo-navigation-bullets-bar',
32385                     cn : [
32386                         {
32387                             tag : 'ul',
32388                             cls : 'roo-navigation-bar'
32389                         }
32390                     ]
32391                 },
32392                 
32393                 {
32394                     tag : 'div',
32395                     cls : 'roo-navigation-bottom-bar'
32396                 }
32397             ]
32398             
32399         };
32400         
32401         return cfg;
32402         
32403     },
32404     
32405     initEvents: function() 
32406     {
32407         
32408     },
32409     
32410     onRender : function(ct, position) 
32411     {
32412         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32413         
32414         if(this.bullets.length){
32415             Roo.each(this.bullets, function(b){
32416                this.addItem(b);
32417             }, this);
32418         }
32419         
32420         this.format();
32421         
32422     },
32423     
32424     addItem : function(cfg)
32425     {
32426         var item = new Roo.bootstrap.NavProgressItem(cfg);
32427         
32428         item.parentId = this.id;
32429         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32430         
32431         if(cfg.html){
32432             var top = new Roo.bootstrap.Element({
32433                 tag : 'div',
32434                 cls : 'roo-navigation-bar-text'
32435             });
32436             
32437             var bottom = new Roo.bootstrap.Element({
32438                 tag : 'div',
32439                 cls : 'roo-navigation-bar-text'
32440             });
32441             
32442             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32443             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32444             
32445             var topText = new Roo.bootstrap.Element({
32446                 tag : 'span',
32447                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32448             });
32449             
32450             var bottomText = new Roo.bootstrap.Element({
32451                 tag : 'span',
32452                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32453             });
32454             
32455             topText.onRender(top.el, null);
32456             bottomText.onRender(bottom.el, null);
32457             
32458             item.topEl = top;
32459             item.bottomEl = bottom;
32460         }
32461         
32462         this.barItems.push(item);
32463         
32464         return item;
32465     },
32466     
32467     getActive : function()
32468     {
32469         var active = false;
32470         
32471         Roo.each(this.barItems, function(v){
32472             
32473             if (!v.isActive()) {
32474                 return;
32475             }
32476             
32477             active = v;
32478             return false;
32479             
32480         });
32481         
32482         return active;
32483     },
32484     
32485     setActiveItem : function(item)
32486     {
32487         var prev = false;
32488         
32489         Roo.each(this.barItems, function(v){
32490             if (v.rid == item.rid) {
32491                 return ;
32492             }
32493             
32494             if (v.isActive()) {
32495                 v.setActive(false);
32496                 prev = v;
32497             }
32498         });
32499
32500         item.setActive(true);
32501         
32502         this.fireEvent('changed', this, item, prev);
32503     },
32504     
32505     getBarItem: function(rid)
32506     {
32507         var ret = false;
32508         
32509         Roo.each(this.barItems, function(e) {
32510             if (e.rid != rid) {
32511                 return;
32512             }
32513             
32514             ret =  e;
32515             return false;
32516         });
32517         
32518         return ret;
32519     },
32520     
32521     indexOfItem : function(item)
32522     {
32523         var index = false;
32524         
32525         Roo.each(this.barItems, function(v, i){
32526             
32527             if (v.rid != item.rid) {
32528                 return;
32529             }
32530             
32531             index = i;
32532             return false
32533         });
32534         
32535         return index;
32536     },
32537     
32538     setActiveNext : function()
32539     {
32540         var i = this.indexOfItem(this.getActive());
32541         
32542         if (i > this.barItems.length) {
32543             return;
32544         }
32545         
32546         this.setActiveItem(this.barItems[i+1]);
32547     },
32548     
32549     setActivePrev : function()
32550     {
32551         var i = this.indexOfItem(this.getActive());
32552         
32553         if (i  < 1) {
32554             return;
32555         }
32556         
32557         this.setActiveItem(this.barItems[i-1]);
32558     },
32559     
32560     format : function()
32561     {
32562         if(!this.barItems.length){
32563             return;
32564         }
32565      
32566         var width = 100 / this.barItems.length;
32567         
32568         Roo.each(this.barItems, function(i){
32569             i.el.setStyle('width', width + '%');
32570             i.topEl.el.setStyle('width', width + '%');
32571             i.bottomEl.el.setStyle('width', width + '%');
32572         }, this);
32573         
32574     }
32575     
32576 });
32577 /*
32578  * - LGPL
32579  *
32580  * Nav Progress Item
32581  * 
32582  */
32583
32584 /**
32585  * @class Roo.bootstrap.NavProgressItem
32586  * @extends Roo.bootstrap.Component
32587  * Bootstrap NavProgressItem class
32588  * @cfg {String} rid the reference id
32589  * @cfg {Boolean} active (true|false) Is item active default false
32590  * @cfg {Boolean} disabled (true|false) Is item active default false
32591  * @cfg {String} html
32592  * @cfg {String} position (top|bottom) text position default bottom
32593  * @cfg {String} icon show icon instead of number
32594  * 
32595  * @constructor
32596  * Create a new NavProgressItem
32597  * @param {Object} config The config object
32598  */
32599 Roo.bootstrap.NavProgressItem = function(config){
32600     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32601     this.addEvents({
32602         // raw events
32603         /**
32604          * @event click
32605          * The raw click event for the entire grid.
32606          * @param {Roo.bootstrap.NavProgressItem} this
32607          * @param {Roo.EventObject} e
32608          */
32609         "click" : true
32610     });
32611    
32612 };
32613
32614 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32615     
32616     rid : '',
32617     active : false,
32618     disabled : false,
32619     html : '',
32620     position : 'bottom',
32621     icon : false,
32622     
32623     getAutoCreate : function()
32624     {
32625         var iconCls = 'roo-navigation-bar-item-icon';
32626         
32627         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32628         
32629         var cfg = {
32630             tag: 'li',
32631             cls: 'roo-navigation-bar-item',
32632             cn : [
32633                 {
32634                     tag : 'i',
32635                     cls : iconCls
32636                 }
32637             ]
32638         };
32639         
32640         if(this.active){
32641             cfg.cls += ' active';
32642         }
32643         if(this.disabled){
32644             cfg.cls += ' disabled';
32645         }
32646         
32647         return cfg;
32648     },
32649     
32650     disable : function()
32651     {
32652         this.setDisabled(true);
32653     },
32654     
32655     enable : function()
32656     {
32657         this.setDisabled(false);
32658     },
32659     
32660     initEvents: function() 
32661     {
32662         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32663         
32664         this.iconEl.on('click', this.onClick, this);
32665     },
32666     
32667     onClick : function(e)
32668     {
32669         e.preventDefault();
32670         
32671         if(this.disabled){
32672             return;
32673         }
32674         
32675         if(this.fireEvent('click', this, e) === false){
32676             return;
32677         };
32678         
32679         this.parent().setActiveItem(this);
32680     },
32681     
32682     isActive: function () 
32683     {
32684         return this.active;
32685     },
32686     
32687     setActive : function(state)
32688     {
32689         if(this.active == state){
32690             return;
32691         }
32692         
32693         this.active = state;
32694         
32695         if (state) {
32696             this.el.addClass('active');
32697             return;
32698         }
32699         
32700         this.el.removeClass('active');
32701         
32702         return;
32703     },
32704     
32705     setDisabled : function(state)
32706     {
32707         if(this.disabled == state){
32708             return;
32709         }
32710         
32711         this.disabled = state;
32712         
32713         if (state) {
32714             this.el.addClass('disabled');
32715             return;
32716         }
32717         
32718         this.el.removeClass('disabled');
32719     },
32720     
32721     tooltipEl : function()
32722     {
32723         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32724     }
32725 });
32726  
32727
32728  /*
32729  * - LGPL
32730  *
32731  * FieldLabel
32732  * 
32733  */
32734
32735 /**
32736  * @class Roo.bootstrap.FieldLabel
32737  * @extends Roo.bootstrap.Component
32738  * Bootstrap FieldLabel class
32739  * @cfg {String} html contents of the element
32740  * @cfg {String} tag tag of the element default label
32741  * @cfg {String} cls class of the element
32742  * @cfg {String} target label target 
32743  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32744  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32745  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32746  * @cfg {String} iconTooltip default "This field is required"
32747  * @cfg {String} indicatorpos (left|right) default left
32748  * 
32749  * @constructor
32750  * Create a new FieldLabel
32751  * @param {Object} config The config object
32752  */
32753
32754 Roo.bootstrap.FieldLabel = function(config){
32755     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32756     
32757     this.addEvents({
32758             /**
32759              * @event invalid
32760              * Fires after the field has been marked as invalid.
32761              * @param {Roo.form.FieldLabel} this
32762              * @param {String} msg The validation message
32763              */
32764             invalid : true,
32765             /**
32766              * @event valid
32767              * Fires after the field has been validated with no errors.
32768              * @param {Roo.form.FieldLabel} this
32769              */
32770             valid : true
32771         });
32772 };
32773
32774 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32775     
32776     tag: 'label',
32777     cls: '',
32778     html: '',
32779     target: '',
32780     allowBlank : true,
32781     invalidClass : 'has-warning',
32782     validClass : 'has-success',
32783     iconTooltip : 'This field is required',
32784     indicatorpos : 'left',
32785     
32786     getAutoCreate : function(){
32787         
32788         var cls = "";
32789         if (!this.allowBlank) {
32790             cls  = "visible";
32791         }
32792         
32793         var cfg = {
32794             tag : this.tag,
32795             cls : 'roo-bootstrap-field-label ' + this.cls,
32796             for : this.target,
32797             cn : [
32798                 {
32799                     tag : 'i',
32800                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32801                     tooltip : this.iconTooltip
32802                 },
32803                 {
32804                     tag : 'span',
32805                     html : this.html
32806                 }
32807             ] 
32808         };
32809         
32810         if(this.indicatorpos == 'right'){
32811             var cfg = {
32812                 tag : this.tag,
32813                 cls : 'roo-bootstrap-field-label ' + this.cls,
32814                 for : this.target,
32815                 cn : [
32816                     {
32817                         tag : 'span',
32818                         html : this.html
32819                     },
32820                     {
32821                         tag : 'i',
32822                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32823                         tooltip : this.iconTooltip
32824                     }
32825                 ] 
32826             };
32827         }
32828         
32829         return cfg;
32830     },
32831     
32832     initEvents: function() 
32833     {
32834         Roo.bootstrap.Element.superclass.initEvents.call(this);
32835         
32836         this.indicator = this.indicatorEl();
32837         
32838         if(this.indicator){
32839             this.indicator.removeClass('visible');
32840             this.indicator.addClass('invisible');
32841         }
32842         
32843         Roo.bootstrap.FieldLabel.register(this);
32844     },
32845     
32846     indicatorEl : function()
32847     {
32848         var indicator = this.el.select('i.roo-required-indicator',true).first();
32849         
32850         if(!indicator){
32851             return false;
32852         }
32853         
32854         return indicator;
32855         
32856     },
32857     
32858     /**
32859      * Mark this field as valid
32860      */
32861     markValid : function()
32862     {
32863         if(this.indicator){
32864             this.indicator.removeClass('visible');
32865             this.indicator.addClass('invisible');
32866         }
32867         if (Roo.bootstrap.version == 3) {
32868             this.el.removeClass(this.invalidClass);
32869             this.el.addClass(this.validClass);
32870         } else {
32871             this.el.removeClass('is-invalid');
32872             this.el.addClass('is-valid');
32873         }
32874         
32875         
32876         this.fireEvent('valid', this);
32877     },
32878     
32879     /**
32880      * Mark this field as invalid
32881      * @param {String} msg The validation message
32882      */
32883     markInvalid : function(msg)
32884     {
32885         if(this.indicator){
32886             this.indicator.removeClass('invisible');
32887             this.indicator.addClass('visible');
32888         }
32889           if (Roo.bootstrap.version == 3) {
32890             this.el.removeClass(this.validClass);
32891             this.el.addClass(this.invalidClass);
32892         } else {
32893             this.el.removeClass('is-valid');
32894             this.el.addClass('is-invalid');
32895         }
32896         
32897         
32898         this.fireEvent('invalid', this, msg);
32899     }
32900     
32901    
32902 });
32903
32904 Roo.apply(Roo.bootstrap.FieldLabel, {
32905     
32906     groups: {},
32907     
32908      /**
32909     * register a FieldLabel Group
32910     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32911     */
32912     register : function(label)
32913     {
32914         if(this.groups.hasOwnProperty(label.target)){
32915             return;
32916         }
32917      
32918         this.groups[label.target] = label;
32919         
32920     },
32921     /**
32922     * fetch a FieldLabel Group based on the target
32923     * @param {string} target
32924     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32925     */
32926     get: function(target) {
32927         if (typeof(this.groups[target]) == 'undefined') {
32928             return false;
32929         }
32930         
32931         return this.groups[target] ;
32932     }
32933 });
32934
32935  
32936
32937  /*
32938  * - LGPL
32939  *
32940  * page DateSplitField.
32941  * 
32942  */
32943
32944
32945 /**
32946  * @class Roo.bootstrap.DateSplitField
32947  * @extends Roo.bootstrap.Component
32948  * Bootstrap DateSplitField class
32949  * @cfg {string} fieldLabel - the label associated
32950  * @cfg {Number} labelWidth set the width of label (0-12)
32951  * @cfg {String} labelAlign (top|left)
32952  * @cfg {Boolean} dayAllowBlank (true|false) default false
32953  * @cfg {Boolean} monthAllowBlank (true|false) default false
32954  * @cfg {Boolean} yearAllowBlank (true|false) default false
32955  * @cfg {string} dayPlaceholder 
32956  * @cfg {string} monthPlaceholder
32957  * @cfg {string} yearPlaceholder
32958  * @cfg {string} dayFormat default 'd'
32959  * @cfg {string} monthFormat default 'm'
32960  * @cfg {string} yearFormat default 'Y'
32961  * @cfg {Number} labellg set the width of label (1-12)
32962  * @cfg {Number} labelmd set the width of label (1-12)
32963  * @cfg {Number} labelsm set the width of label (1-12)
32964  * @cfg {Number} labelxs set the width of label (1-12)
32965
32966  *     
32967  * @constructor
32968  * Create a new DateSplitField
32969  * @param {Object} config The config object
32970  */
32971
32972 Roo.bootstrap.DateSplitField = function(config){
32973     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32974     
32975     this.addEvents({
32976         // raw events
32977          /**
32978          * @event years
32979          * getting the data of years
32980          * @param {Roo.bootstrap.DateSplitField} this
32981          * @param {Object} years
32982          */
32983         "years" : true,
32984         /**
32985          * @event days
32986          * getting the data of days
32987          * @param {Roo.bootstrap.DateSplitField} this
32988          * @param {Object} days
32989          */
32990         "days" : true,
32991         /**
32992          * @event invalid
32993          * Fires after the field has been marked as invalid.
32994          * @param {Roo.form.Field} this
32995          * @param {String} msg The validation message
32996          */
32997         invalid : true,
32998        /**
32999          * @event valid
33000          * Fires after the field has been validated with no errors.
33001          * @param {Roo.form.Field} this
33002          */
33003         valid : true
33004     });
33005 };
33006
33007 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33008     
33009     fieldLabel : '',
33010     labelAlign : 'top',
33011     labelWidth : 3,
33012     dayAllowBlank : false,
33013     monthAllowBlank : false,
33014     yearAllowBlank : false,
33015     dayPlaceholder : '',
33016     monthPlaceholder : '',
33017     yearPlaceholder : '',
33018     dayFormat : 'd',
33019     monthFormat : 'm',
33020     yearFormat : 'Y',
33021     isFormField : true,
33022     labellg : 0,
33023     labelmd : 0,
33024     labelsm : 0,
33025     labelxs : 0,
33026     
33027     getAutoCreate : function()
33028     {
33029         var cfg = {
33030             tag : 'div',
33031             cls : 'row roo-date-split-field-group',
33032             cn : [
33033                 {
33034                     tag : 'input',
33035                     type : 'hidden',
33036                     cls : 'form-hidden-field roo-date-split-field-group-value',
33037                     name : this.name
33038                 }
33039             ]
33040         };
33041         
33042         var labelCls = 'col-md-12';
33043         var contentCls = 'col-md-4';
33044         
33045         if(this.fieldLabel){
33046             
33047             var label = {
33048                 tag : 'div',
33049                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33050                 cn : [
33051                     {
33052                         tag : 'label',
33053                         html : this.fieldLabel
33054                     }
33055                 ]
33056             };
33057             
33058             if(this.labelAlign == 'left'){
33059             
33060                 if(this.labelWidth > 12){
33061                     label.style = "width: " + this.labelWidth + 'px';
33062                 }
33063
33064                 if(this.labelWidth < 13 && this.labelmd == 0){
33065                     this.labelmd = this.labelWidth;
33066                 }
33067
33068                 if(this.labellg > 0){
33069                     labelCls = ' col-lg-' + this.labellg;
33070                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33071                 }
33072
33073                 if(this.labelmd > 0){
33074                     labelCls = ' col-md-' + this.labelmd;
33075                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33076                 }
33077
33078                 if(this.labelsm > 0){
33079                     labelCls = ' col-sm-' + this.labelsm;
33080                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33081                 }
33082
33083                 if(this.labelxs > 0){
33084                     labelCls = ' col-xs-' + this.labelxs;
33085                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33086                 }
33087             }
33088             
33089             label.cls += ' ' + labelCls;
33090             
33091             cfg.cn.push(label);
33092         }
33093         
33094         Roo.each(['day', 'month', 'year'], function(t){
33095             cfg.cn.push({
33096                 tag : 'div',
33097                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33098             });
33099         }, this);
33100         
33101         return cfg;
33102     },
33103     
33104     inputEl: function ()
33105     {
33106         return this.el.select('.roo-date-split-field-group-value', true).first();
33107     },
33108     
33109     onRender : function(ct, position) 
33110     {
33111         var _this = this;
33112         
33113         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33114         
33115         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33116         
33117         this.dayField = new Roo.bootstrap.ComboBox({
33118             allowBlank : this.dayAllowBlank,
33119             alwaysQuery : true,
33120             displayField : 'value',
33121             editable : false,
33122             fieldLabel : '',
33123             forceSelection : true,
33124             mode : 'local',
33125             placeholder : this.dayPlaceholder,
33126             selectOnFocus : true,
33127             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33128             triggerAction : 'all',
33129             typeAhead : true,
33130             valueField : 'value',
33131             store : new Roo.data.SimpleStore({
33132                 data : (function() {    
33133                     var days = [];
33134                     _this.fireEvent('days', _this, days);
33135                     return days;
33136                 })(),
33137                 fields : [ 'value' ]
33138             }),
33139             listeners : {
33140                 select : function (_self, record, index)
33141                 {
33142                     _this.setValue(_this.getValue());
33143                 }
33144             }
33145         });
33146
33147         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33148         
33149         this.monthField = new Roo.bootstrap.MonthField({
33150             after : '<i class=\"fa fa-calendar\"></i>',
33151             allowBlank : this.monthAllowBlank,
33152             placeholder : this.monthPlaceholder,
33153             readOnly : true,
33154             listeners : {
33155                 render : function (_self)
33156                 {
33157                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33158                         e.preventDefault();
33159                         _self.focus();
33160                     });
33161                 },
33162                 select : function (_self, oldvalue, newvalue)
33163                 {
33164                     _this.setValue(_this.getValue());
33165                 }
33166             }
33167         });
33168         
33169         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33170         
33171         this.yearField = new Roo.bootstrap.ComboBox({
33172             allowBlank : this.yearAllowBlank,
33173             alwaysQuery : true,
33174             displayField : 'value',
33175             editable : false,
33176             fieldLabel : '',
33177             forceSelection : true,
33178             mode : 'local',
33179             placeholder : this.yearPlaceholder,
33180             selectOnFocus : true,
33181             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33182             triggerAction : 'all',
33183             typeAhead : true,
33184             valueField : 'value',
33185             store : new Roo.data.SimpleStore({
33186                 data : (function() {
33187                     var years = [];
33188                     _this.fireEvent('years', _this, years);
33189                     return years;
33190                 })(),
33191                 fields : [ 'value' ]
33192             }),
33193             listeners : {
33194                 select : function (_self, record, index)
33195                 {
33196                     _this.setValue(_this.getValue());
33197                 }
33198             }
33199         });
33200
33201         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33202     },
33203     
33204     setValue : function(v, format)
33205     {
33206         this.inputEl.dom.value = v;
33207         
33208         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33209         
33210         var d = Date.parseDate(v, f);
33211         
33212         if(!d){
33213             this.validate();
33214             return;
33215         }
33216         
33217         this.setDay(d.format(this.dayFormat));
33218         this.setMonth(d.format(this.monthFormat));
33219         this.setYear(d.format(this.yearFormat));
33220         
33221         this.validate();
33222         
33223         return;
33224     },
33225     
33226     setDay : function(v)
33227     {
33228         this.dayField.setValue(v);
33229         this.inputEl.dom.value = this.getValue();
33230         this.validate();
33231         return;
33232     },
33233     
33234     setMonth : function(v)
33235     {
33236         this.monthField.setValue(v, true);
33237         this.inputEl.dom.value = this.getValue();
33238         this.validate();
33239         return;
33240     },
33241     
33242     setYear : function(v)
33243     {
33244         this.yearField.setValue(v);
33245         this.inputEl.dom.value = this.getValue();
33246         this.validate();
33247         return;
33248     },
33249     
33250     getDay : function()
33251     {
33252         return this.dayField.getValue();
33253     },
33254     
33255     getMonth : function()
33256     {
33257         return this.monthField.getValue();
33258     },
33259     
33260     getYear : function()
33261     {
33262         return this.yearField.getValue();
33263     },
33264     
33265     getValue : function()
33266     {
33267         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33268         
33269         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33270         
33271         return date;
33272     },
33273     
33274     reset : function()
33275     {
33276         this.setDay('');
33277         this.setMonth('');
33278         this.setYear('');
33279         this.inputEl.dom.value = '';
33280         this.validate();
33281         return;
33282     },
33283     
33284     validate : function()
33285     {
33286         var d = this.dayField.validate();
33287         var m = this.monthField.validate();
33288         var y = this.yearField.validate();
33289         
33290         var valid = true;
33291         
33292         if(
33293                 (!this.dayAllowBlank && !d) ||
33294                 (!this.monthAllowBlank && !m) ||
33295                 (!this.yearAllowBlank && !y)
33296         ){
33297             valid = false;
33298         }
33299         
33300         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33301             return valid;
33302         }
33303         
33304         if(valid){
33305             this.markValid();
33306             return valid;
33307         }
33308         
33309         this.markInvalid();
33310         
33311         return valid;
33312     },
33313     
33314     markValid : function()
33315     {
33316         
33317         var label = this.el.select('label', true).first();
33318         var icon = this.el.select('i.fa-star', true).first();
33319
33320         if(label && icon){
33321             icon.remove();
33322         }
33323         
33324         this.fireEvent('valid', this);
33325     },
33326     
33327      /**
33328      * Mark this field as invalid
33329      * @param {String} msg The validation message
33330      */
33331     markInvalid : function(msg)
33332     {
33333         
33334         var label = this.el.select('label', true).first();
33335         var icon = this.el.select('i.fa-star', true).first();
33336
33337         if(label && !icon){
33338             this.el.select('.roo-date-split-field-label', true).createChild({
33339                 tag : 'i',
33340                 cls : 'text-danger fa fa-lg fa-star',
33341                 tooltip : 'This field is required',
33342                 style : 'margin-right:5px;'
33343             }, label, true);
33344         }
33345         
33346         this.fireEvent('invalid', this, msg);
33347     },
33348     
33349     clearInvalid : function()
33350     {
33351         var label = this.el.select('label', true).first();
33352         var icon = this.el.select('i.fa-star', true).first();
33353
33354         if(label && icon){
33355             icon.remove();
33356         }
33357         
33358         this.fireEvent('valid', this);
33359     },
33360     
33361     getName: function()
33362     {
33363         return this.name;
33364     }
33365     
33366 });
33367
33368  /**
33369  *
33370  * This is based on 
33371  * http://masonry.desandro.com
33372  *
33373  * The idea is to render all the bricks based on vertical width...
33374  *
33375  * The original code extends 'outlayer' - we might need to use that....
33376  * 
33377  */
33378
33379
33380 /**
33381  * @class Roo.bootstrap.LayoutMasonry
33382  * @extends Roo.bootstrap.Component
33383  * Bootstrap Layout Masonry class
33384  * 
33385  * @constructor
33386  * Create a new Element
33387  * @param {Object} config The config object
33388  */
33389
33390 Roo.bootstrap.LayoutMasonry = function(config){
33391     
33392     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33393     
33394     this.bricks = [];
33395     
33396     Roo.bootstrap.LayoutMasonry.register(this);
33397     
33398     this.addEvents({
33399         // raw events
33400         /**
33401          * @event layout
33402          * Fire after layout the items
33403          * @param {Roo.bootstrap.LayoutMasonry} this
33404          * @param {Roo.EventObject} e
33405          */
33406         "layout" : true
33407     });
33408     
33409 };
33410
33411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33412     
33413     /**
33414      * @cfg {Boolean} isLayoutInstant = no animation?
33415      */   
33416     isLayoutInstant : false, // needed?
33417    
33418     /**
33419      * @cfg {Number} boxWidth  width of the columns
33420      */   
33421     boxWidth : 450,
33422     
33423       /**
33424      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33425      */   
33426     boxHeight : 0,
33427     
33428     /**
33429      * @cfg {Number} padWidth padding below box..
33430      */   
33431     padWidth : 10, 
33432     
33433     /**
33434      * @cfg {Number} gutter gutter width..
33435      */   
33436     gutter : 10,
33437     
33438      /**
33439      * @cfg {Number} maxCols maximum number of columns
33440      */   
33441     
33442     maxCols: 0,
33443     
33444     /**
33445      * @cfg {Boolean} isAutoInitial defalut true
33446      */   
33447     isAutoInitial : true, 
33448     
33449     containerWidth: 0,
33450     
33451     /**
33452      * @cfg {Boolean} isHorizontal defalut false
33453      */   
33454     isHorizontal : false, 
33455
33456     currentSize : null,
33457     
33458     tag: 'div',
33459     
33460     cls: '',
33461     
33462     bricks: null, //CompositeElement
33463     
33464     cols : 1,
33465     
33466     _isLayoutInited : false,
33467     
33468 //    isAlternative : false, // only use for vertical layout...
33469     
33470     /**
33471      * @cfg {Number} alternativePadWidth padding below box..
33472      */   
33473     alternativePadWidth : 50,
33474     
33475     selectedBrick : [],
33476     
33477     getAutoCreate : function(){
33478         
33479         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33480         
33481         var cfg = {
33482             tag: this.tag,
33483             cls: 'blog-masonary-wrapper ' + this.cls,
33484             cn : {
33485                 cls : 'mas-boxes masonary'
33486             }
33487         };
33488         
33489         return cfg;
33490     },
33491     
33492     getChildContainer: function( )
33493     {
33494         if (this.boxesEl) {
33495             return this.boxesEl;
33496         }
33497         
33498         this.boxesEl = this.el.select('.mas-boxes').first();
33499         
33500         return this.boxesEl;
33501     },
33502     
33503     
33504     initEvents : function()
33505     {
33506         var _this = this;
33507         
33508         if(this.isAutoInitial){
33509             Roo.log('hook children rendered');
33510             this.on('childrenrendered', function() {
33511                 Roo.log('children rendered');
33512                 _this.initial();
33513             } ,this);
33514         }
33515     },
33516     
33517     initial : function()
33518     {
33519         this.selectedBrick = [];
33520         
33521         this.currentSize = this.el.getBox(true);
33522         
33523         Roo.EventManager.onWindowResize(this.resize, this); 
33524
33525         if(!this.isAutoInitial){
33526             this.layout();
33527             return;
33528         }
33529         
33530         this.layout();
33531         
33532         return;
33533         //this.layout.defer(500,this);
33534         
33535     },
33536     
33537     resize : function()
33538     {
33539         var cs = this.el.getBox(true);
33540         
33541         if (
33542                 this.currentSize.width == cs.width && 
33543                 this.currentSize.x == cs.x && 
33544                 this.currentSize.height == cs.height && 
33545                 this.currentSize.y == cs.y 
33546         ) {
33547             Roo.log("no change in with or X or Y");
33548             return;
33549         }
33550         
33551         this.currentSize = cs;
33552         
33553         this.layout();
33554         
33555     },
33556     
33557     layout : function()
33558     {   
33559         this._resetLayout();
33560         
33561         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33562         
33563         this.layoutItems( isInstant );
33564       
33565         this._isLayoutInited = true;
33566         
33567         this.fireEvent('layout', this);
33568         
33569     },
33570     
33571     _resetLayout : function()
33572     {
33573         if(this.isHorizontal){
33574             this.horizontalMeasureColumns();
33575             return;
33576         }
33577         
33578         this.verticalMeasureColumns();
33579         
33580     },
33581     
33582     verticalMeasureColumns : function()
33583     {
33584         this.getContainerWidth();
33585         
33586 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33587 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33588 //            return;
33589 //        }
33590         
33591         var boxWidth = this.boxWidth + this.padWidth;
33592         
33593         if(this.containerWidth < this.boxWidth){
33594             boxWidth = this.containerWidth
33595         }
33596         
33597         var containerWidth = this.containerWidth;
33598         
33599         var cols = Math.floor(containerWidth / boxWidth);
33600         
33601         this.cols = Math.max( cols, 1 );
33602         
33603         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33604         
33605         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33606         
33607         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33608         
33609         this.colWidth = boxWidth + avail - this.padWidth;
33610         
33611         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33612         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33613     },
33614     
33615     horizontalMeasureColumns : function()
33616     {
33617         this.getContainerWidth();
33618         
33619         var boxWidth = this.boxWidth;
33620         
33621         if(this.containerWidth < boxWidth){
33622             boxWidth = this.containerWidth;
33623         }
33624         
33625         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33626         
33627         this.el.setHeight(boxWidth);
33628         
33629     },
33630     
33631     getContainerWidth : function()
33632     {
33633         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33634     },
33635     
33636     layoutItems : function( isInstant )
33637     {
33638         Roo.log(this.bricks);
33639         
33640         var items = Roo.apply([], this.bricks);
33641         
33642         if(this.isHorizontal){
33643             this._horizontalLayoutItems( items , isInstant );
33644             return;
33645         }
33646         
33647 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33648 //            this._verticalAlternativeLayoutItems( items , isInstant );
33649 //            return;
33650 //        }
33651         
33652         this._verticalLayoutItems( items , isInstant );
33653         
33654     },
33655     
33656     _verticalLayoutItems : function ( items , isInstant)
33657     {
33658         if ( !items || !items.length ) {
33659             return;
33660         }
33661         
33662         var standard = [
33663             ['xs', 'xs', 'xs', 'tall'],
33664             ['xs', 'xs', 'tall'],
33665             ['xs', 'xs', 'sm'],
33666             ['xs', 'xs', 'xs'],
33667             ['xs', 'tall'],
33668             ['xs', 'sm'],
33669             ['xs', 'xs'],
33670             ['xs'],
33671             
33672             ['sm', 'xs', 'xs'],
33673             ['sm', 'xs'],
33674             ['sm'],
33675             
33676             ['tall', 'xs', 'xs', 'xs'],
33677             ['tall', 'xs', 'xs'],
33678             ['tall', 'xs'],
33679             ['tall']
33680             
33681         ];
33682         
33683         var queue = [];
33684         
33685         var boxes = [];
33686         
33687         var box = [];
33688         
33689         Roo.each(items, function(item, k){
33690             
33691             switch (item.size) {
33692                 // these layouts take up a full box,
33693                 case 'md' :
33694                 case 'md-left' :
33695                 case 'md-right' :
33696                 case 'wide' :
33697                     
33698                     if(box.length){
33699                         boxes.push(box);
33700                         box = [];
33701                     }
33702                     
33703                     boxes.push([item]);
33704                     
33705                     break;
33706                     
33707                 case 'xs' :
33708                 case 'sm' :
33709                 case 'tall' :
33710                     
33711                     box.push(item);
33712                     
33713                     break;
33714                 default :
33715                     break;
33716                     
33717             }
33718             
33719         }, this);
33720         
33721         if(box.length){
33722             boxes.push(box);
33723             box = [];
33724         }
33725         
33726         var filterPattern = function(box, length)
33727         {
33728             if(!box.length){
33729                 return;
33730             }
33731             
33732             var match = false;
33733             
33734             var pattern = box.slice(0, length);
33735             
33736             var format = [];
33737             
33738             Roo.each(pattern, function(i){
33739                 format.push(i.size);
33740             }, this);
33741             
33742             Roo.each(standard, function(s){
33743                 
33744                 if(String(s) != String(format)){
33745                     return;
33746                 }
33747                 
33748                 match = true;
33749                 return false;
33750                 
33751             }, this);
33752             
33753             if(!match && length == 1){
33754                 return;
33755             }
33756             
33757             if(!match){
33758                 filterPattern(box, length - 1);
33759                 return;
33760             }
33761                 
33762             queue.push(pattern);
33763
33764             box = box.slice(length, box.length);
33765
33766             filterPattern(box, 4);
33767
33768             return;
33769             
33770         }
33771         
33772         Roo.each(boxes, function(box, k){
33773             
33774             if(!box.length){
33775                 return;
33776             }
33777             
33778             if(box.length == 1){
33779                 queue.push(box);
33780                 return;
33781             }
33782             
33783             filterPattern(box, 4);
33784             
33785         }, this);
33786         
33787         this._processVerticalLayoutQueue( queue, isInstant );
33788         
33789     },
33790     
33791 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33792 //    {
33793 //        if ( !items || !items.length ) {
33794 //            return;
33795 //        }
33796 //
33797 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33798 //        
33799 //    },
33800     
33801     _horizontalLayoutItems : function ( items , isInstant)
33802     {
33803         if ( !items || !items.length || items.length < 3) {
33804             return;
33805         }
33806         
33807         items.reverse();
33808         
33809         var eItems = items.slice(0, 3);
33810         
33811         items = items.slice(3, items.length);
33812         
33813         var standard = [
33814             ['xs', 'xs', 'xs', 'wide'],
33815             ['xs', 'xs', 'wide'],
33816             ['xs', 'xs', 'sm'],
33817             ['xs', 'xs', 'xs'],
33818             ['xs', 'wide'],
33819             ['xs', 'sm'],
33820             ['xs', 'xs'],
33821             ['xs'],
33822             
33823             ['sm', 'xs', 'xs'],
33824             ['sm', 'xs'],
33825             ['sm'],
33826             
33827             ['wide', 'xs', 'xs', 'xs'],
33828             ['wide', 'xs', 'xs'],
33829             ['wide', 'xs'],
33830             ['wide'],
33831             
33832             ['wide-thin']
33833         ];
33834         
33835         var queue = [];
33836         
33837         var boxes = [];
33838         
33839         var box = [];
33840         
33841         Roo.each(items, function(item, k){
33842             
33843             switch (item.size) {
33844                 case 'md' :
33845                 case 'md-left' :
33846                 case 'md-right' :
33847                 case 'tall' :
33848                     
33849                     if(box.length){
33850                         boxes.push(box);
33851                         box = [];
33852                     }
33853                     
33854                     boxes.push([item]);
33855                     
33856                     break;
33857                     
33858                 case 'xs' :
33859                 case 'sm' :
33860                 case 'wide' :
33861                 case 'wide-thin' :
33862                     
33863                     box.push(item);
33864                     
33865                     break;
33866                 default :
33867                     break;
33868                     
33869             }
33870             
33871         }, this);
33872         
33873         if(box.length){
33874             boxes.push(box);
33875             box = [];
33876         }
33877         
33878         var filterPattern = function(box, length)
33879         {
33880             if(!box.length){
33881                 return;
33882             }
33883             
33884             var match = false;
33885             
33886             var pattern = box.slice(0, length);
33887             
33888             var format = [];
33889             
33890             Roo.each(pattern, function(i){
33891                 format.push(i.size);
33892             }, this);
33893             
33894             Roo.each(standard, function(s){
33895                 
33896                 if(String(s) != String(format)){
33897                     return;
33898                 }
33899                 
33900                 match = true;
33901                 return false;
33902                 
33903             }, this);
33904             
33905             if(!match && length == 1){
33906                 return;
33907             }
33908             
33909             if(!match){
33910                 filterPattern(box, length - 1);
33911                 return;
33912             }
33913                 
33914             queue.push(pattern);
33915
33916             box = box.slice(length, box.length);
33917
33918             filterPattern(box, 4);
33919
33920             return;
33921             
33922         }
33923         
33924         Roo.each(boxes, function(box, k){
33925             
33926             if(!box.length){
33927                 return;
33928             }
33929             
33930             if(box.length == 1){
33931                 queue.push(box);
33932                 return;
33933             }
33934             
33935             filterPattern(box, 4);
33936             
33937         }, this);
33938         
33939         
33940         var prune = [];
33941         
33942         var pos = this.el.getBox(true);
33943         
33944         var minX = pos.x;
33945         
33946         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33947         
33948         var hit_end = false;
33949         
33950         Roo.each(queue, function(box){
33951             
33952             if(hit_end){
33953                 
33954                 Roo.each(box, function(b){
33955                 
33956                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33957                     b.el.hide();
33958
33959                 }, this);
33960
33961                 return;
33962             }
33963             
33964             var mx = 0;
33965             
33966             Roo.each(box, function(b){
33967                 
33968                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33969                 b.el.show();
33970
33971                 mx = Math.max(mx, b.x);
33972                 
33973             }, this);
33974             
33975             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33976             
33977             if(maxX < minX){
33978                 
33979                 Roo.each(box, function(b){
33980                 
33981                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33982                     b.el.hide();
33983                     
33984                 }, this);
33985                 
33986                 hit_end = true;
33987                 
33988                 return;
33989             }
33990             
33991             prune.push(box);
33992             
33993         }, this);
33994         
33995         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33996     },
33997     
33998     /** Sets position of item in DOM
33999     * @param {Element} item
34000     * @param {Number} x - horizontal position
34001     * @param {Number} y - vertical position
34002     * @param {Boolean} isInstant - disables transitions
34003     */
34004     _processVerticalLayoutQueue : function( queue, isInstant )
34005     {
34006         var pos = this.el.getBox(true);
34007         var x = pos.x;
34008         var y = pos.y;
34009         var maxY = [];
34010         
34011         for (var i = 0; i < this.cols; i++){
34012             maxY[i] = pos.y;
34013         }
34014         
34015         Roo.each(queue, function(box, k){
34016             
34017             var col = k % this.cols;
34018             
34019             Roo.each(box, function(b,kk){
34020                 
34021                 b.el.position('absolute');
34022                 
34023                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34024                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34025                 
34026                 if(b.size == 'md-left' || b.size == 'md-right'){
34027                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34028                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34029                 }
34030                 
34031                 b.el.setWidth(width);
34032                 b.el.setHeight(height);
34033                 // iframe?
34034                 b.el.select('iframe',true).setSize(width,height);
34035                 
34036             }, this);
34037             
34038             for (var i = 0; i < this.cols; i++){
34039                 
34040                 if(maxY[i] < maxY[col]){
34041                     col = i;
34042                     continue;
34043                 }
34044                 
34045                 col = Math.min(col, i);
34046                 
34047             }
34048             
34049             x = pos.x + col * (this.colWidth + this.padWidth);
34050             
34051             y = maxY[col];
34052             
34053             var positions = [];
34054             
34055             switch (box.length){
34056                 case 1 :
34057                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34058                     break;
34059                 case 2 :
34060                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34061                     break;
34062                 case 3 :
34063                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34064                     break;
34065                 case 4 :
34066                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34067                     break;
34068                 default :
34069                     break;
34070             }
34071             
34072             Roo.each(box, function(b,kk){
34073                 
34074                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34075                 
34076                 var sz = b.el.getSize();
34077                 
34078                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34079                 
34080             }, this);
34081             
34082         }, this);
34083         
34084         var mY = 0;
34085         
34086         for (var i = 0; i < this.cols; i++){
34087             mY = Math.max(mY, maxY[i]);
34088         }
34089         
34090         this.el.setHeight(mY - pos.y);
34091         
34092     },
34093     
34094 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34095 //    {
34096 //        var pos = this.el.getBox(true);
34097 //        var x = pos.x;
34098 //        var y = pos.y;
34099 //        var maxX = pos.right;
34100 //        
34101 //        var maxHeight = 0;
34102 //        
34103 //        Roo.each(items, function(item, k){
34104 //            
34105 //            var c = k % 2;
34106 //            
34107 //            item.el.position('absolute');
34108 //                
34109 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34110 //
34111 //            item.el.setWidth(width);
34112 //
34113 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34114 //
34115 //            item.el.setHeight(height);
34116 //            
34117 //            if(c == 0){
34118 //                item.el.setXY([x, y], isInstant ? false : true);
34119 //            } else {
34120 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34121 //            }
34122 //            
34123 //            y = y + height + this.alternativePadWidth;
34124 //            
34125 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34126 //            
34127 //        }, this);
34128 //        
34129 //        this.el.setHeight(maxHeight);
34130 //        
34131 //    },
34132     
34133     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34134     {
34135         var pos = this.el.getBox(true);
34136         
34137         var minX = pos.x;
34138         var minY = pos.y;
34139         
34140         var maxX = pos.right;
34141         
34142         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34143         
34144         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34145         
34146         Roo.each(queue, function(box, k){
34147             
34148             Roo.each(box, function(b, kk){
34149                 
34150                 b.el.position('absolute');
34151                 
34152                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34153                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34154                 
34155                 if(b.size == 'md-left' || b.size == 'md-right'){
34156                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34157                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34158                 }
34159                 
34160                 b.el.setWidth(width);
34161                 b.el.setHeight(height);
34162                 
34163             }, this);
34164             
34165             if(!box.length){
34166                 return;
34167             }
34168             
34169             var positions = [];
34170             
34171             switch (box.length){
34172                 case 1 :
34173                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34174                     break;
34175                 case 2 :
34176                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34177                     break;
34178                 case 3 :
34179                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34180                     break;
34181                 case 4 :
34182                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34183                     break;
34184                 default :
34185                     break;
34186             }
34187             
34188             Roo.each(box, function(b,kk){
34189                 
34190                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34191                 
34192                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34193                 
34194             }, this);
34195             
34196         }, this);
34197         
34198     },
34199     
34200     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34201     {
34202         Roo.each(eItems, function(b,k){
34203             
34204             b.size = (k == 0) ? 'sm' : 'xs';
34205             b.x = (k == 0) ? 2 : 1;
34206             b.y = (k == 0) ? 2 : 1;
34207             
34208             b.el.position('absolute');
34209             
34210             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34211                 
34212             b.el.setWidth(width);
34213             
34214             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34215             
34216             b.el.setHeight(height);
34217             
34218         }, this);
34219
34220         var positions = [];
34221         
34222         positions.push({
34223             x : maxX - this.unitWidth * 2 - this.gutter,
34224             y : minY
34225         });
34226         
34227         positions.push({
34228             x : maxX - this.unitWidth,
34229             y : minY + (this.unitWidth + this.gutter) * 2
34230         });
34231         
34232         positions.push({
34233             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34234             y : minY
34235         });
34236         
34237         Roo.each(eItems, function(b,k){
34238             
34239             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34240
34241         }, this);
34242         
34243     },
34244     
34245     getVerticalOneBoxColPositions : function(x, y, box)
34246     {
34247         var pos = [];
34248         
34249         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34250         
34251         if(box[0].size == 'md-left'){
34252             rand = 0;
34253         }
34254         
34255         if(box[0].size == 'md-right'){
34256             rand = 1;
34257         }
34258         
34259         pos.push({
34260             x : x + (this.unitWidth + this.gutter) * rand,
34261             y : y
34262         });
34263         
34264         return pos;
34265     },
34266     
34267     getVerticalTwoBoxColPositions : function(x, y, box)
34268     {
34269         var pos = [];
34270         
34271         if(box[0].size == 'xs'){
34272             
34273             pos.push({
34274                 x : x,
34275                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34276             });
34277
34278             pos.push({
34279                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34280                 y : y
34281             });
34282             
34283             return pos;
34284             
34285         }
34286         
34287         pos.push({
34288             x : x,
34289             y : y
34290         });
34291
34292         pos.push({
34293             x : x + (this.unitWidth + this.gutter) * 2,
34294             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34295         });
34296         
34297         return pos;
34298         
34299     },
34300     
34301     getVerticalThreeBoxColPositions : function(x, y, box)
34302     {
34303         var pos = [];
34304         
34305         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34306             
34307             pos.push({
34308                 x : x,
34309                 y : y
34310             });
34311
34312             pos.push({
34313                 x : x + (this.unitWidth + this.gutter) * 1,
34314                 y : y
34315             });
34316             
34317             pos.push({
34318                 x : x + (this.unitWidth + this.gutter) * 2,
34319                 y : y
34320             });
34321             
34322             return pos;
34323             
34324         }
34325         
34326         if(box[0].size == 'xs' && box[1].size == 'xs'){
34327             
34328             pos.push({
34329                 x : x,
34330                 y : y
34331             });
34332
34333             pos.push({
34334                 x : x,
34335                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34336             });
34337             
34338             pos.push({
34339                 x : x + (this.unitWidth + this.gutter) * 1,
34340                 y : y
34341             });
34342             
34343             return pos;
34344             
34345         }
34346         
34347         pos.push({
34348             x : x,
34349             y : y
34350         });
34351
34352         pos.push({
34353             x : x + (this.unitWidth + this.gutter) * 2,
34354             y : y
34355         });
34356
34357         pos.push({
34358             x : x + (this.unitWidth + this.gutter) * 2,
34359             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34360         });
34361             
34362         return pos;
34363         
34364     },
34365     
34366     getVerticalFourBoxColPositions : function(x, y, box)
34367     {
34368         var pos = [];
34369         
34370         if(box[0].size == 'xs'){
34371             
34372             pos.push({
34373                 x : x,
34374                 y : y
34375             });
34376
34377             pos.push({
34378                 x : x,
34379                 y : y + (this.unitHeight + this.gutter) * 1
34380             });
34381             
34382             pos.push({
34383                 x : x,
34384                 y : y + (this.unitHeight + this.gutter) * 2
34385             });
34386             
34387             pos.push({
34388                 x : x + (this.unitWidth + this.gutter) * 1,
34389                 y : y
34390             });
34391             
34392             return pos;
34393             
34394         }
34395         
34396         pos.push({
34397             x : x,
34398             y : y
34399         });
34400
34401         pos.push({
34402             x : x + (this.unitWidth + this.gutter) * 2,
34403             y : y
34404         });
34405
34406         pos.push({
34407             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34408             y : y + (this.unitHeight + this.gutter) * 1
34409         });
34410
34411         pos.push({
34412             x : x + (this.unitWidth + this.gutter) * 2,
34413             y : y + (this.unitWidth + this.gutter) * 2
34414         });
34415
34416         return pos;
34417         
34418     },
34419     
34420     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34421     {
34422         var pos = [];
34423         
34424         if(box[0].size == 'md-left'){
34425             pos.push({
34426                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34427                 y : minY
34428             });
34429             
34430             return pos;
34431         }
34432         
34433         if(box[0].size == 'md-right'){
34434             pos.push({
34435                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34436                 y : minY + (this.unitWidth + this.gutter) * 1
34437             });
34438             
34439             return pos;
34440         }
34441         
34442         var rand = Math.floor(Math.random() * (4 - box[0].y));
34443         
34444         pos.push({
34445             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34446             y : minY + (this.unitWidth + this.gutter) * rand
34447         });
34448         
34449         return pos;
34450         
34451     },
34452     
34453     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34454     {
34455         var pos = [];
34456         
34457         if(box[0].size == 'xs'){
34458             
34459             pos.push({
34460                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34461                 y : minY
34462             });
34463
34464             pos.push({
34465                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34466                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34467             });
34468             
34469             return pos;
34470             
34471         }
34472         
34473         pos.push({
34474             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34475             y : minY
34476         });
34477
34478         pos.push({
34479             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34480             y : minY + (this.unitWidth + this.gutter) * 2
34481         });
34482         
34483         return pos;
34484         
34485     },
34486     
34487     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34488     {
34489         var pos = [];
34490         
34491         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34492             
34493             pos.push({
34494                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34495                 y : minY
34496             });
34497
34498             pos.push({
34499                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34500                 y : minY + (this.unitWidth + this.gutter) * 1
34501             });
34502             
34503             pos.push({
34504                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34505                 y : minY + (this.unitWidth + this.gutter) * 2
34506             });
34507             
34508             return pos;
34509             
34510         }
34511         
34512         if(box[0].size == 'xs' && box[1].size == 'xs'){
34513             
34514             pos.push({
34515                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34516                 y : minY
34517             });
34518
34519             pos.push({
34520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34521                 y : minY
34522             });
34523             
34524             pos.push({
34525                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34526                 y : minY + (this.unitWidth + this.gutter) * 1
34527             });
34528             
34529             return pos;
34530             
34531         }
34532         
34533         pos.push({
34534             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34535             y : minY
34536         });
34537
34538         pos.push({
34539             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34540             y : minY + (this.unitWidth + this.gutter) * 2
34541         });
34542
34543         pos.push({
34544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34545             y : minY + (this.unitWidth + this.gutter) * 2
34546         });
34547             
34548         return pos;
34549         
34550     },
34551     
34552     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34553     {
34554         var pos = [];
34555         
34556         if(box[0].size == 'xs'){
34557             
34558             pos.push({
34559                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34560                 y : minY
34561             });
34562
34563             pos.push({
34564                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34565                 y : minY
34566             });
34567             
34568             pos.push({
34569                 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),
34570                 y : minY
34571             });
34572             
34573             pos.push({
34574                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34575                 y : minY + (this.unitWidth + this.gutter) * 1
34576             });
34577             
34578             return pos;
34579             
34580         }
34581         
34582         pos.push({
34583             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34584             y : minY
34585         });
34586         
34587         pos.push({
34588             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34589             y : minY + (this.unitWidth + this.gutter) * 2
34590         });
34591         
34592         pos.push({
34593             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34594             y : minY + (this.unitWidth + this.gutter) * 2
34595         });
34596         
34597         pos.push({
34598             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),
34599             y : minY + (this.unitWidth + this.gutter) * 2
34600         });
34601
34602         return pos;
34603         
34604     },
34605     
34606     /**
34607     * remove a Masonry Brick
34608     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34609     */
34610     removeBrick : function(brick_id)
34611     {
34612         if (!brick_id) {
34613             return;
34614         }
34615         
34616         for (var i = 0; i<this.bricks.length; i++) {
34617             if (this.bricks[i].id == brick_id) {
34618                 this.bricks.splice(i,1);
34619                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34620                 this.initial();
34621             }
34622         }
34623     },
34624     
34625     /**
34626     * adds a Masonry Brick
34627     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34628     */
34629     addBrick : function(cfg)
34630     {
34631         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34632         //this.register(cn);
34633         cn.parentId = this.id;
34634         cn.render(this.el);
34635         return cn;
34636     },
34637     
34638     /**
34639     * register a Masonry Brick
34640     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34641     */
34642     
34643     register : function(brick)
34644     {
34645         this.bricks.push(brick);
34646         brick.masonryId = this.id;
34647     },
34648     
34649     /**
34650     * clear all the Masonry Brick
34651     */
34652     clearAll : function()
34653     {
34654         this.bricks = [];
34655         //this.getChildContainer().dom.innerHTML = "";
34656         this.el.dom.innerHTML = '';
34657     },
34658     
34659     getSelected : function()
34660     {
34661         if (!this.selectedBrick) {
34662             return false;
34663         }
34664         
34665         return this.selectedBrick;
34666     }
34667 });
34668
34669 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34670     
34671     groups: {},
34672      /**
34673     * register a Masonry Layout
34674     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34675     */
34676     
34677     register : function(layout)
34678     {
34679         this.groups[layout.id] = layout;
34680     },
34681     /**
34682     * fetch a  Masonry Layout based on the masonry layout ID
34683     * @param {string} the masonry layout to add
34684     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34685     */
34686     
34687     get: function(layout_id) {
34688         if (typeof(this.groups[layout_id]) == 'undefined') {
34689             return false;
34690         }
34691         return this.groups[layout_id] ;
34692     }
34693     
34694     
34695     
34696 });
34697
34698  
34699
34700  /**
34701  *
34702  * This is based on 
34703  * http://masonry.desandro.com
34704  *
34705  * The idea is to render all the bricks based on vertical width...
34706  *
34707  * The original code extends 'outlayer' - we might need to use that....
34708  * 
34709  */
34710
34711
34712 /**
34713  * @class Roo.bootstrap.LayoutMasonryAuto
34714  * @extends Roo.bootstrap.Component
34715  * Bootstrap Layout Masonry class
34716  * 
34717  * @constructor
34718  * Create a new Element
34719  * @param {Object} config The config object
34720  */
34721
34722 Roo.bootstrap.LayoutMasonryAuto = function(config){
34723     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34724 };
34725
34726 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34727     
34728       /**
34729      * @cfg {Boolean} isFitWidth  - resize the width..
34730      */   
34731     isFitWidth : false,  // options..
34732     /**
34733      * @cfg {Boolean} isOriginLeft = left align?
34734      */   
34735     isOriginLeft : true,
34736     /**
34737      * @cfg {Boolean} isOriginTop = top align?
34738      */   
34739     isOriginTop : false,
34740     /**
34741      * @cfg {Boolean} isLayoutInstant = no animation?
34742      */   
34743     isLayoutInstant : false, // needed?
34744     /**
34745      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34746      */   
34747     isResizingContainer : true,
34748     /**
34749      * @cfg {Number} columnWidth  width of the columns 
34750      */   
34751     
34752     columnWidth : 0,
34753     
34754     /**
34755      * @cfg {Number} maxCols maximum number of columns
34756      */   
34757     
34758     maxCols: 0,
34759     /**
34760      * @cfg {Number} padHeight padding below box..
34761      */   
34762     
34763     padHeight : 10, 
34764     
34765     /**
34766      * @cfg {Boolean} isAutoInitial defalut true
34767      */   
34768     
34769     isAutoInitial : true, 
34770     
34771     // private?
34772     gutter : 0,
34773     
34774     containerWidth: 0,
34775     initialColumnWidth : 0,
34776     currentSize : null,
34777     
34778     colYs : null, // array.
34779     maxY : 0,
34780     padWidth: 10,
34781     
34782     
34783     tag: 'div',
34784     cls: '',
34785     bricks: null, //CompositeElement
34786     cols : 0, // array?
34787     // element : null, // wrapped now this.el
34788     _isLayoutInited : null, 
34789     
34790     
34791     getAutoCreate : function(){
34792         
34793         var cfg = {
34794             tag: this.tag,
34795             cls: 'blog-masonary-wrapper ' + this.cls,
34796             cn : {
34797                 cls : 'mas-boxes masonary'
34798             }
34799         };
34800         
34801         return cfg;
34802     },
34803     
34804     getChildContainer: function( )
34805     {
34806         if (this.boxesEl) {
34807             return this.boxesEl;
34808         }
34809         
34810         this.boxesEl = this.el.select('.mas-boxes').first();
34811         
34812         return this.boxesEl;
34813     },
34814     
34815     
34816     initEvents : function()
34817     {
34818         var _this = this;
34819         
34820         if(this.isAutoInitial){
34821             Roo.log('hook children rendered');
34822             this.on('childrenrendered', function() {
34823                 Roo.log('children rendered');
34824                 _this.initial();
34825             } ,this);
34826         }
34827         
34828     },
34829     
34830     initial : function()
34831     {
34832         this.reloadItems();
34833
34834         this.currentSize = this.el.getBox(true);
34835
34836         /// was window resize... - let's see if this works..
34837         Roo.EventManager.onWindowResize(this.resize, this); 
34838
34839         if(!this.isAutoInitial){
34840             this.layout();
34841             return;
34842         }
34843         
34844         this.layout.defer(500,this);
34845     },
34846     
34847     reloadItems: function()
34848     {
34849         this.bricks = this.el.select('.masonry-brick', true);
34850         
34851         this.bricks.each(function(b) {
34852             //Roo.log(b.getSize());
34853             if (!b.attr('originalwidth')) {
34854                 b.attr('originalwidth',  b.getSize().width);
34855             }
34856             
34857         });
34858         
34859         Roo.log(this.bricks.elements.length);
34860     },
34861     
34862     resize : function()
34863     {
34864         Roo.log('resize');
34865         var cs = this.el.getBox(true);
34866         
34867         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34868             Roo.log("no change in with or X");
34869             return;
34870         }
34871         this.currentSize = cs;
34872         this.layout();
34873     },
34874     
34875     layout : function()
34876     {
34877          Roo.log('layout');
34878         this._resetLayout();
34879         //this._manageStamps();
34880       
34881         // don't animate first layout
34882         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34883         this.layoutItems( isInstant );
34884       
34885         // flag for initalized
34886         this._isLayoutInited = true;
34887     },
34888     
34889     layoutItems : function( isInstant )
34890     {
34891         //var items = this._getItemsForLayout( this.items );
34892         // original code supports filtering layout items.. we just ignore it..
34893         
34894         this._layoutItems( this.bricks , isInstant );
34895       
34896         this._postLayout();
34897     },
34898     _layoutItems : function ( items , isInstant)
34899     {
34900        //this.fireEvent( 'layout', this, items );
34901     
34902
34903         if ( !items || !items.elements.length ) {
34904           // no items, emit event with empty array
34905             return;
34906         }
34907
34908         var queue = [];
34909         items.each(function(item) {
34910             Roo.log("layout item");
34911             Roo.log(item);
34912             // get x/y object from method
34913             var position = this._getItemLayoutPosition( item );
34914             // enqueue
34915             position.item = item;
34916             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34917             queue.push( position );
34918         }, this);
34919       
34920         this._processLayoutQueue( queue );
34921     },
34922     /** Sets position of item in DOM
34923     * @param {Element} item
34924     * @param {Number} x - horizontal position
34925     * @param {Number} y - vertical position
34926     * @param {Boolean} isInstant - disables transitions
34927     */
34928     _processLayoutQueue : function( queue )
34929     {
34930         for ( var i=0, len = queue.length; i < len; i++ ) {
34931             var obj = queue[i];
34932             obj.item.position('absolute');
34933             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34934         }
34935     },
34936       
34937     
34938     /**
34939     * Any logic you want to do after each layout,
34940     * i.e. size the container
34941     */
34942     _postLayout : function()
34943     {
34944         this.resizeContainer();
34945     },
34946     
34947     resizeContainer : function()
34948     {
34949         if ( !this.isResizingContainer ) {
34950             return;
34951         }
34952         var size = this._getContainerSize();
34953         if ( size ) {
34954             this.el.setSize(size.width,size.height);
34955             this.boxesEl.setSize(size.width,size.height);
34956         }
34957     },
34958     
34959     
34960     
34961     _resetLayout : function()
34962     {
34963         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34964         this.colWidth = this.el.getWidth();
34965         //this.gutter = this.el.getWidth(); 
34966         
34967         this.measureColumns();
34968
34969         // reset column Y
34970         var i = this.cols;
34971         this.colYs = [];
34972         while (i--) {
34973             this.colYs.push( 0 );
34974         }
34975     
34976         this.maxY = 0;
34977     },
34978
34979     measureColumns : function()
34980     {
34981         this.getContainerWidth();
34982       // if columnWidth is 0, default to outerWidth of first item
34983         if ( !this.columnWidth ) {
34984             var firstItem = this.bricks.first();
34985             Roo.log(firstItem);
34986             this.columnWidth  = this.containerWidth;
34987             if (firstItem && firstItem.attr('originalwidth') ) {
34988                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34989             }
34990             // columnWidth fall back to item of first element
34991             Roo.log("set column width?");
34992                         this.initialColumnWidth = this.columnWidth  ;
34993
34994             // if first elem has no width, default to size of container
34995             
34996         }
34997         
34998         
34999         if (this.initialColumnWidth) {
35000             this.columnWidth = this.initialColumnWidth;
35001         }
35002         
35003         
35004             
35005         // column width is fixed at the top - however if container width get's smaller we should
35006         // reduce it...
35007         
35008         // this bit calcs how man columns..
35009             
35010         var columnWidth = this.columnWidth += this.gutter;
35011       
35012         // calculate columns
35013         var containerWidth = this.containerWidth + this.gutter;
35014         
35015         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35016         // fix rounding errors, typically with gutters
35017         var excess = columnWidth - containerWidth % columnWidth;
35018         
35019         
35020         // if overshoot is less than a pixel, round up, otherwise floor it
35021         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35022         cols = Math[ mathMethod ]( cols );
35023         this.cols = Math.max( cols, 1 );
35024         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35025         
35026          // padding positioning..
35027         var totalColWidth = this.cols * this.columnWidth;
35028         var padavail = this.containerWidth - totalColWidth;
35029         // so for 2 columns - we need 3 'pads'
35030         
35031         var padNeeded = (1+this.cols) * this.padWidth;
35032         
35033         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35034         
35035         this.columnWidth += padExtra
35036         //this.padWidth = Math.floor(padavail /  ( this.cols));
35037         
35038         // adjust colum width so that padding is fixed??
35039         
35040         // we have 3 columns ... total = width * 3
35041         // we have X left over... that should be used by 
35042         
35043         //if (this.expandC) {
35044             
35045         //}
35046         
35047         
35048         
35049     },
35050     
35051     getContainerWidth : function()
35052     {
35053        /* // container is parent if fit width
35054         var container = this.isFitWidth ? this.element.parentNode : this.element;
35055         // check that this.size and size are there
35056         // IE8 triggers resize on body size change, so they might not be
35057         
35058         var size = getSize( container );  //FIXME
35059         this.containerWidth = size && size.innerWidth; //FIXME
35060         */
35061          
35062         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35063         
35064     },
35065     
35066     _getItemLayoutPosition : function( item )  // what is item?
35067     {
35068         // we resize the item to our columnWidth..
35069       
35070         item.setWidth(this.columnWidth);
35071         item.autoBoxAdjust  = false;
35072         
35073         var sz = item.getSize();
35074  
35075         // how many columns does this brick span
35076         var remainder = this.containerWidth % this.columnWidth;
35077         
35078         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35079         // round if off by 1 pixel, otherwise use ceil
35080         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35081         colSpan = Math.min( colSpan, this.cols );
35082         
35083         // normally this should be '1' as we dont' currently allow multi width columns..
35084         
35085         var colGroup = this._getColGroup( colSpan );
35086         // get the minimum Y value from the columns
35087         var minimumY = Math.min.apply( Math, colGroup );
35088         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35089         
35090         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35091          
35092         // position the brick
35093         var position = {
35094             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35095             y: this.currentSize.y + minimumY + this.padHeight
35096         };
35097         
35098         Roo.log(position);
35099         // apply setHeight to necessary columns
35100         var setHeight = minimumY + sz.height + this.padHeight;
35101         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35102         
35103         var setSpan = this.cols + 1 - colGroup.length;
35104         for ( var i = 0; i < setSpan; i++ ) {
35105           this.colYs[ shortColIndex + i ] = setHeight ;
35106         }
35107       
35108         return position;
35109     },
35110     
35111     /**
35112      * @param {Number} colSpan - number of columns the element spans
35113      * @returns {Array} colGroup
35114      */
35115     _getColGroup : function( colSpan )
35116     {
35117         if ( colSpan < 2 ) {
35118           // if brick spans only one column, use all the column Ys
35119           return this.colYs;
35120         }
35121       
35122         var colGroup = [];
35123         // how many different places could this brick fit horizontally
35124         var groupCount = this.cols + 1 - colSpan;
35125         // for each group potential horizontal position
35126         for ( var i = 0; i < groupCount; i++ ) {
35127           // make an array of colY values for that one group
35128           var groupColYs = this.colYs.slice( i, i + colSpan );
35129           // and get the max value of the array
35130           colGroup[i] = Math.max.apply( Math, groupColYs );
35131         }
35132         return colGroup;
35133     },
35134     /*
35135     _manageStamp : function( stamp )
35136     {
35137         var stampSize =  stamp.getSize();
35138         var offset = stamp.getBox();
35139         // get the columns that this stamp affects
35140         var firstX = this.isOriginLeft ? offset.x : offset.right;
35141         var lastX = firstX + stampSize.width;
35142         var firstCol = Math.floor( firstX / this.columnWidth );
35143         firstCol = Math.max( 0, firstCol );
35144         
35145         var lastCol = Math.floor( lastX / this.columnWidth );
35146         // lastCol should not go over if multiple of columnWidth #425
35147         lastCol -= lastX % this.columnWidth ? 0 : 1;
35148         lastCol = Math.min( this.cols - 1, lastCol );
35149         
35150         // set colYs to bottom of the stamp
35151         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35152             stampSize.height;
35153             
35154         for ( var i = firstCol; i <= lastCol; i++ ) {
35155           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35156         }
35157     },
35158     */
35159     
35160     _getContainerSize : function()
35161     {
35162         this.maxY = Math.max.apply( Math, this.colYs );
35163         var size = {
35164             height: this.maxY
35165         };
35166       
35167         if ( this.isFitWidth ) {
35168             size.width = this._getContainerFitWidth();
35169         }
35170       
35171         return size;
35172     },
35173     
35174     _getContainerFitWidth : function()
35175     {
35176         var unusedCols = 0;
35177         // count unused columns
35178         var i = this.cols;
35179         while ( --i ) {
35180           if ( this.colYs[i] !== 0 ) {
35181             break;
35182           }
35183           unusedCols++;
35184         }
35185         // fit container to columns that have been used
35186         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35187     },
35188     
35189     needsResizeLayout : function()
35190     {
35191         var previousWidth = this.containerWidth;
35192         this.getContainerWidth();
35193         return previousWidth !== this.containerWidth;
35194     }
35195  
35196 });
35197
35198  
35199
35200  /*
35201  * - LGPL
35202  *
35203  * element
35204  * 
35205  */
35206
35207 /**
35208  * @class Roo.bootstrap.MasonryBrick
35209  * @extends Roo.bootstrap.Component
35210  * Bootstrap MasonryBrick class
35211  * 
35212  * @constructor
35213  * Create a new MasonryBrick
35214  * @param {Object} config The config object
35215  */
35216
35217 Roo.bootstrap.MasonryBrick = function(config){
35218     
35219     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35220     
35221     Roo.bootstrap.MasonryBrick.register(this);
35222     
35223     this.addEvents({
35224         // raw events
35225         /**
35226          * @event click
35227          * When a MasonryBrick is clcik
35228          * @param {Roo.bootstrap.MasonryBrick} this
35229          * @param {Roo.EventObject} e
35230          */
35231         "click" : true
35232     });
35233 };
35234
35235 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35236     
35237     /**
35238      * @cfg {String} title
35239      */   
35240     title : '',
35241     /**
35242      * @cfg {String} html
35243      */   
35244     html : '',
35245     /**
35246      * @cfg {String} bgimage
35247      */   
35248     bgimage : '',
35249     /**
35250      * @cfg {String} videourl
35251      */   
35252     videourl : '',
35253     /**
35254      * @cfg {String} cls
35255      */   
35256     cls : '',
35257     /**
35258      * @cfg {String} href
35259      */   
35260     href : '',
35261     /**
35262      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35263      */   
35264     size : 'xs',
35265     
35266     /**
35267      * @cfg {String} placetitle (center|bottom)
35268      */   
35269     placetitle : '',
35270     
35271     /**
35272      * @cfg {Boolean} isFitContainer defalut true
35273      */   
35274     isFitContainer : true, 
35275     
35276     /**
35277      * @cfg {Boolean} preventDefault defalut false
35278      */   
35279     preventDefault : false, 
35280     
35281     /**
35282      * @cfg {Boolean} inverse defalut false
35283      */   
35284     maskInverse : false, 
35285     
35286     getAutoCreate : function()
35287     {
35288         if(!this.isFitContainer){
35289             return this.getSplitAutoCreate();
35290         }
35291         
35292         var cls = 'masonry-brick masonry-brick-full';
35293         
35294         if(this.href.length){
35295             cls += ' masonry-brick-link';
35296         }
35297         
35298         if(this.bgimage.length){
35299             cls += ' masonry-brick-image';
35300         }
35301         
35302         if(this.maskInverse){
35303             cls += ' mask-inverse';
35304         }
35305         
35306         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35307             cls += ' enable-mask';
35308         }
35309         
35310         if(this.size){
35311             cls += ' masonry-' + this.size + '-brick';
35312         }
35313         
35314         if(this.placetitle.length){
35315             
35316             switch (this.placetitle) {
35317                 case 'center' :
35318                     cls += ' masonry-center-title';
35319                     break;
35320                 case 'bottom' :
35321                     cls += ' masonry-bottom-title';
35322                     break;
35323                 default:
35324                     break;
35325             }
35326             
35327         } else {
35328             if(!this.html.length && !this.bgimage.length){
35329                 cls += ' masonry-center-title';
35330             }
35331
35332             if(!this.html.length && this.bgimage.length){
35333                 cls += ' masonry-bottom-title';
35334             }
35335         }
35336         
35337         if(this.cls){
35338             cls += ' ' + this.cls;
35339         }
35340         
35341         var cfg = {
35342             tag: (this.href.length) ? 'a' : 'div',
35343             cls: cls,
35344             cn: [
35345                 {
35346                     tag: 'div',
35347                     cls: 'masonry-brick-mask'
35348                 },
35349                 {
35350                     tag: 'div',
35351                     cls: 'masonry-brick-paragraph',
35352                     cn: []
35353                 }
35354             ]
35355         };
35356         
35357         if(this.href.length){
35358             cfg.href = this.href;
35359         }
35360         
35361         var cn = cfg.cn[1].cn;
35362         
35363         if(this.title.length){
35364             cn.push({
35365                 tag: 'h4',
35366                 cls: 'masonry-brick-title',
35367                 html: this.title
35368             });
35369         }
35370         
35371         if(this.html.length){
35372             cn.push({
35373                 tag: 'p',
35374                 cls: 'masonry-brick-text',
35375                 html: this.html
35376             });
35377         }
35378         
35379         if (!this.title.length && !this.html.length) {
35380             cfg.cn[1].cls += ' hide';
35381         }
35382         
35383         if(this.bgimage.length){
35384             cfg.cn.push({
35385                 tag: 'img',
35386                 cls: 'masonry-brick-image-view',
35387                 src: this.bgimage
35388             });
35389         }
35390         
35391         if(this.videourl.length){
35392             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35393             // youtube support only?
35394             cfg.cn.push({
35395                 tag: 'iframe',
35396                 cls: 'masonry-brick-image-view',
35397                 src: vurl,
35398                 frameborder : 0,
35399                 allowfullscreen : true
35400             });
35401         }
35402         
35403         return cfg;
35404         
35405     },
35406     
35407     getSplitAutoCreate : function()
35408     {
35409         var cls = 'masonry-brick masonry-brick-split';
35410         
35411         if(this.href.length){
35412             cls += ' masonry-brick-link';
35413         }
35414         
35415         if(this.bgimage.length){
35416             cls += ' masonry-brick-image';
35417         }
35418         
35419         if(this.size){
35420             cls += ' masonry-' + this.size + '-brick';
35421         }
35422         
35423         switch (this.placetitle) {
35424             case 'center' :
35425                 cls += ' masonry-center-title';
35426                 break;
35427             case 'bottom' :
35428                 cls += ' masonry-bottom-title';
35429                 break;
35430             default:
35431                 if(!this.bgimage.length){
35432                     cls += ' masonry-center-title';
35433                 }
35434
35435                 if(this.bgimage.length){
35436                     cls += ' masonry-bottom-title';
35437                 }
35438                 break;
35439         }
35440         
35441         if(this.cls){
35442             cls += ' ' + this.cls;
35443         }
35444         
35445         var cfg = {
35446             tag: (this.href.length) ? 'a' : 'div',
35447             cls: cls,
35448             cn: [
35449                 {
35450                     tag: 'div',
35451                     cls: 'masonry-brick-split-head',
35452                     cn: [
35453                         {
35454                             tag: 'div',
35455                             cls: 'masonry-brick-paragraph',
35456                             cn: []
35457                         }
35458                     ]
35459                 },
35460                 {
35461                     tag: 'div',
35462                     cls: 'masonry-brick-split-body',
35463                     cn: []
35464                 }
35465             ]
35466         };
35467         
35468         if(this.href.length){
35469             cfg.href = this.href;
35470         }
35471         
35472         if(this.title.length){
35473             cfg.cn[0].cn[0].cn.push({
35474                 tag: 'h4',
35475                 cls: 'masonry-brick-title',
35476                 html: this.title
35477             });
35478         }
35479         
35480         if(this.html.length){
35481             cfg.cn[1].cn.push({
35482                 tag: 'p',
35483                 cls: 'masonry-brick-text',
35484                 html: this.html
35485             });
35486         }
35487
35488         if(this.bgimage.length){
35489             cfg.cn[0].cn.push({
35490                 tag: 'img',
35491                 cls: 'masonry-brick-image-view',
35492                 src: this.bgimage
35493             });
35494         }
35495         
35496         if(this.videourl.length){
35497             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35498             // youtube support only?
35499             cfg.cn[0].cn.cn.push({
35500                 tag: 'iframe',
35501                 cls: 'masonry-brick-image-view',
35502                 src: vurl,
35503                 frameborder : 0,
35504                 allowfullscreen : true
35505             });
35506         }
35507         
35508         return cfg;
35509     },
35510     
35511     initEvents: function() 
35512     {
35513         switch (this.size) {
35514             case 'xs' :
35515                 this.x = 1;
35516                 this.y = 1;
35517                 break;
35518             case 'sm' :
35519                 this.x = 2;
35520                 this.y = 2;
35521                 break;
35522             case 'md' :
35523             case 'md-left' :
35524             case 'md-right' :
35525                 this.x = 3;
35526                 this.y = 3;
35527                 break;
35528             case 'tall' :
35529                 this.x = 2;
35530                 this.y = 3;
35531                 break;
35532             case 'wide' :
35533                 this.x = 3;
35534                 this.y = 2;
35535                 break;
35536             case 'wide-thin' :
35537                 this.x = 3;
35538                 this.y = 1;
35539                 break;
35540                         
35541             default :
35542                 break;
35543         }
35544         
35545         if(Roo.isTouch){
35546             this.el.on('touchstart', this.onTouchStart, this);
35547             this.el.on('touchmove', this.onTouchMove, this);
35548             this.el.on('touchend', this.onTouchEnd, this);
35549             this.el.on('contextmenu', this.onContextMenu, this);
35550         } else {
35551             this.el.on('mouseenter'  ,this.enter, this);
35552             this.el.on('mouseleave', this.leave, this);
35553             this.el.on('click', this.onClick, this);
35554         }
35555         
35556         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35557             this.parent().bricks.push(this);   
35558         }
35559         
35560     },
35561     
35562     onClick: function(e, el)
35563     {
35564         var time = this.endTimer - this.startTimer;
35565         // Roo.log(e.preventDefault());
35566         if(Roo.isTouch){
35567             if(time > 1000){
35568                 e.preventDefault();
35569                 return;
35570             }
35571         }
35572         
35573         if(!this.preventDefault){
35574             return;
35575         }
35576         
35577         e.preventDefault();
35578         
35579         if (this.activeClass != '') {
35580             this.selectBrick();
35581         }
35582         
35583         this.fireEvent('click', this, e);
35584     },
35585     
35586     enter: function(e, el)
35587     {
35588         e.preventDefault();
35589         
35590         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35591             return;
35592         }
35593         
35594         if(this.bgimage.length && this.html.length){
35595             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35596         }
35597     },
35598     
35599     leave: function(e, el)
35600     {
35601         e.preventDefault();
35602         
35603         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35604             return;
35605         }
35606         
35607         if(this.bgimage.length && this.html.length){
35608             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35609         }
35610     },
35611     
35612     onTouchStart: function(e, el)
35613     {
35614 //        e.preventDefault();
35615         
35616         this.touchmoved = false;
35617         
35618         if(!this.isFitContainer){
35619             return;
35620         }
35621         
35622         if(!this.bgimage.length || !this.html.length){
35623             return;
35624         }
35625         
35626         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35627         
35628         this.timer = new Date().getTime();
35629         
35630     },
35631     
35632     onTouchMove: function(e, el)
35633     {
35634         this.touchmoved = true;
35635     },
35636     
35637     onContextMenu : function(e,el)
35638     {
35639         e.preventDefault();
35640         e.stopPropagation();
35641         return false;
35642     },
35643     
35644     onTouchEnd: function(e, el)
35645     {
35646 //        e.preventDefault();
35647         
35648         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35649         
35650             this.leave(e,el);
35651             
35652             return;
35653         }
35654         
35655         if(!this.bgimage.length || !this.html.length){
35656             
35657             if(this.href.length){
35658                 window.location.href = this.href;
35659             }
35660             
35661             return;
35662         }
35663         
35664         if(!this.isFitContainer){
35665             return;
35666         }
35667         
35668         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35669         
35670         window.location.href = this.href;
35671     },
35672     
35673     //selection on single brick only
35674     selectBrick : function() {
35675         
35676         if (!this.parentId) {
35677             return;
35678         }
35679         
35680         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35681         var index = m.selectedBrick.indexOf(this.id);
35682         
35683         if ( index > -1) {
35684             m.selectedBrick.splice(index,1);
35685             this.el.removeClass(this.activeClass);
35686             return;
35687         }
35688         
35689         for(var i = 0; i < m.selectedBrick.length; i++) {
35690             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35691             b.el.removeClass(b.activeClass);
35692         }
35693         
35694         m.selectedBrick = [];
35695         
35696         m.selectedBrick.push(this.id);
35697         this.el.addClass(this.activeClass);
35698         return;
35699     },
35700     
35701     isSelected : function(){
35702         return this.el.hasClass(this.activeClass);
35703         
35704     }
35705 });
35706
35707 Roo.apply(Roo.bootstrap.MasonryBrick, {
35708     
35709     //groups: {},
35710     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35711      /**
35712     * register a Masonry Brick
35713     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35714     */
35715     
35716     register : function(brick)
35717     {
35718         //this.groups[brick.id] = brick;
35719         this.groups.add(brick.id, brick);
35720     },
35721     /**
35722     * fetch a  masonry brick based on the masonry brick ID
35723     * @param {string} the masonry brick to add
35724     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35725     */
35726     
35727     get: function(brick_id) 
35728     {
35729         // if (typeof(this.groups[brick_id]) == 'undefined') {
35730         //     return false;
35731         // }
35732         // return this.groups[brick_id] ;
35733         
35734         if(this.groups.key(brick_id)) {
35735             return this.groups.key(brick_id);
35736         }
35737         
35738         return false;
35739     }
35740     
35741     
35742     
35743 });
35744
35745  /*
35746  * - LGPL
35747  *
35748  * element
35749  * 
35750  */
35751
35752 /**
35753  * @class Roo.bootstrap.Brick
35754  * @extends Roo.bootstrap.Component
35755  * Bootstrap Brick class
35756  * 
35757  * @constructor
35758  * Create a new Brick
35759  * @param {Object} config The config object
35760  */
35761
35762 Roo.bootstrap.Brick = function(config){
35763     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35764     
35765     this.addEvents({
35766         // raw events
35767         /**
35768          * @event click
35769          * When a Brick is click
35770          * @param {Roo.bootstrap.Brick} this
35771          * @param {Roo.EventObject} e
35772          */
35773         "click" : true
35774     });
35775 };
35776
35777 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35778     
35779     /**
35780      * @cfg {String} title
35781      */   
35782     title : '',
35783     /**
35784      * @cfg {String} html
35785      */   
35786     html : '',
35787     /**
35788      * @cfg {String} bgimage
35789      */   
35790     bgimage : '',
35791     /**
35792      * @cfg {String} cls
35793      */   
35794     cls : '',
35795     /**
35796      * @cfg {String} href
35797      */   
35798     href : '',
35799     /**
35800      * @cfg {String} video
35801      */   
35802     video : '',
35803     /**
35804      * @cfg {Boolean} square
35805      */   
35806     square : true,
35807     
35808     getAutoCreate : function()
35809     {
35810         var cls = 'roo-brick';
35811         
35812         if(this.href.length){
35813             cls += ' roo-brick-link';
35814         }
35815         
35816         if(this.bgimage.length){
35817             cls += ' roo-brick-image';
35818         }
35819         
35820         if(!this.html.length && !this.bgimage.length){
35821             cls += ' roo-brick-center-title';
35822         }
35823         
35824         if(!this.html.length && this.bgimage.length){
35825             cls += ' roo-brick-bottom-title';
35826         }
35827         
35828         if(this.cls){
35829             cls += ' ' + this.cls;
35830         }
35831         
35832         var cfg = {
35833             tag: (this.href.length) ? 'a' : 'div',
35834             cls: cls,
35835             cn: [
35836                 {
35837                     tag: 'div',
35838                     cls: 'roo-brick-paragraph',
35839                     cn: []
35840                 }
35841             ]
35842         };
35843         
35844         if(this.href.length){
35845             cfg.href = this.href;
35846         }
35847         
35848         var cn = cfg.cn[0].cn;
35849         
35850         if(this.title.length){
35851             cn.push({
35852                 tag: 'h4',
35853                 cls: 'roo-brick-title',
35854                 html: this.title
35855             });
35856         }
35857         
35858         if(this.html.length){
35859             cn.push({
35860                 tag: 'p',
35861                 cls: 'roo-brick-text',
35862                 html: this.html
35863             });
35864         } else {
35865             cn.cls += ' hide';
35866         }
35867         
35868         if(this.bgimage.length){
35869             cfg.cn.push({
35870                 tag: 'img',
35871                 cls: 'roo-brick-image-view',
35872                 src: this.bgimage
35873             });
35874         }
35875         
35876         return cfg;
35877     },
35878     
35879     initEvents: function() 
35880     {
35881         if(this.title.length || this.html.length){
35882             this.el.on('mouseenter'  ,this.enter, this);
35883             this.el.on('mouseleave', this.leave, this);
35884         }
35885         
35886         Roo.EventManager.onWindowResize(this.resize, this); 
35887         
35888         if(this.bgimage.length){
35889             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35890             this.imageEl.on('load', this.onImageLoad, this);
35891             return;
35892         }
35893         
35894         this.resize();
35895     },
35896     
35897     onImageLoad : function()
35898     {
35899         this.resize();
35900     },
35901     
35902     resize : function()
35903     {
35904         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35905         
35906         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35907         
35908         if(this.bgimage.length){
35909             var image = this.el.select('.roo-brick-image-view', true).first();
35910             
35911             image.setWidth(paragraph.getWidth());
35912             
35913             if(this.square){
35914                 image.setHeight(paragraph.getWidth());
35915             }
35916             
35917             this.el.setHeight(image.getHeight());
35918             paragraph.setHeight(image.getHeight());
35919             
35920         }
35921         
35922     },
35923     
35924     enter: function(e, el)
35925     {
35926         e.preventDefault();
35927         
35928         if(this.bgimage.length){
35929             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35930             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35931         }
35932     },
35933     
35934     leave: function(e, el)
35935     {
35936         e.preventDefault();
35937         
35938         if(this.bgimage.length){
35939             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35940             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35941         }
35942     }
35943     
35944 });
35945
35946  
35947
35948  /*
35949  * - LGPL
35950  *
35951  * Number field 
35952  */
35953
35954 /**
35955  * @class Roo.bootstrap.NumberField
35956  * @extends Roo.bootstrap.Input
35957  * Bootstrap NumberField class
35958  * 
35959  * 
35960  * 
35961  * 
35962  * @constructor
35963  * Create a new NumberField
35964  * @param {Object} config The config object
35965  */
35966
35967 Roo.bootstrap.NumberField = function(config){
35968     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35969 };
35970
35971 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35972     
35973     /**
35974      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35975      */
35976     allowDecimals : true,
35977     /**
35978      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35979      */
35980     decimalSeparator : ".",
35981     /**
35982      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35983      */
35984     decimalPrecision : 2,
35985     /**
35986      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35987      */
35988     allowNegative : true,
35989     
35990     /**
35991      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35992      */
35993     allowZero: true,
35994     /**
35995      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35996      */
35997     minValue : Number.NEGATIVE_INFINITY,
35998     /**
35999      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36000      */
36001     maxValue : Number.MAX_VALUE,
36002     /**
36003      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36004      */
36005     minText : "The minimum value for this field is {0}",
36006     /**
36007      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36008      */
36009     maxText : "The maximum value for this field is {0}",
36010     /**
36011      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36012      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36013      */
36014     nanText : "{0} is not a valid number",
36015     /**
36016      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36017      */
36018     thousandsDelimiter : false,
36019     /**
36020      * @cfg {String} valueAlign alignment of value
36021      */
36022     valueAlign : "left",
36023
36024     getAutoCreate : function()
36025     {
36026         var hiddenInput = {
36027             tag: 'input',
36028             type: 'hidden',
36029             id: Roo.id(),
36030             cls: 'hidden-number-input'
36031         };
36032         
36033         if (this.name) {
36034             hiddenInput.name = this.name;
36035         }
36036         
36037         this.name = '';
36038         
36039         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36040         
36041         this.name = hiddenInput.name;
36042         
36043         if(cfg.cn.length > 0) {
36044             cfg.cn.push(hiddenInput);
36045         }
36046         
36047         return cfg;
36048     },
36049
36050     // private
36051     initEvents : function()
36052     {   
36053         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36054         
36055         var allowed = "0123456789";
36056         
36057         if(this.allowDecimals){
36058             allowed += this.decimalSeparator;
36059         }
36060         
36061         if(this.allowNegative){
36062             allowed += "-";
36063         }
36064         
36065         if(this.thousandsDelimiter) {
36066             allowed += ",";
36067         }
36068         
36069         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36070         
36071         var keyPress = function(e){
36072             
36073             var k = e.getKey();
36074             
36075             var c = e.getCharCode();
36076             
36077             if(
36078                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36079                     allowed.indexOf(String.fromCharCode(c)) === -1
36080             ){
36081                 e.stopEvent();
36082                 return;
36083             }
36084             
36085             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36086                 return;
36087             }
36088             
36089             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36090                 e.stopEvent();
36091             }
36092         };
36093         
36094         this.el.on("keypress", keyPress, this);
36095     },
36096     
36097     validateValue : function(value)
36098     {
36099         
36100         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36101             return false;
36102         }
36103         
36104         var num = this.parseValue(value);
36105         
36106         if(isNaN(num)){
36107             this.markInvalid(String.format(this.nanText, value));
36108             return false;
36109         }
36110         
36111         if(num < this.minValue){
36112             this.markInvalid(String.format(this.minText, this.minValue));
36113             return false;
36114         }
36115         
36116         if(num > this.maxValue){
36117             this.markInvalid(String.format(this.maxText, this.maxValue));
36118             return false;
36119         }
36120         
36121         return true;
36122     },
36123
36124     getValue : function()
36125     {
36126         var v = this.hiddenEl().getValue();
36127         
36128         return this.fixPrecision(this.parseValue(v));
36129     },
36130
36131     parseValue : function(value)
36132     {
36133         if(this.thousandsDelimiter) {
36134             value += "";
36135             r = new RegExp(",", "g");
36136             value = value.replace(r, "");
36137         }
36138         
36139         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36140         return isNaN(value) ? '' : value;
36141     },
36142
36143     fixPrecision : function(value)
36144     {
36145         if(this.thousandsDelimiter) {
36146             value += "";
36147             r = new RegExp(",", "g");
36148             value = value.replace(r, "");
36149         }
36150         
36151         var nan = isNaN(value);
36152         
36153         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36154             return nan ? '' : value;
36155         }
36156         return parseFloat(value).toFixed(this.decimalPrecision);
36157     },
36158
36159     setValue : function(v)
36160     {
36161         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36162         
36163         this.value = v;
36164         
36165         if(this.rendered){
36166             
36167             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36168             
36169             this.inputEl().dom.value = (v == '') ? '' :
36170                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36171             
36172             if(!this.allowZero && v === '0') {
36173                 this.hiddenEl().dom.value = '';
36174                 this.inputEl().dom.value = '';
36175             }
36176             
36177             this.validate();
36178         }
36179     },
36180
36181     decimalPrecisionFcn : function(v)
36182     {
36183         return Math.floor(v);
36184     },
36185
36186     beforeBlur : function()
36187     {
36188         var v = this.parseValue(this.getRawValue());
36189         
36190         if(v || v === 0 || v === ''){
36191             this.setValue(v);
36192         }
36193     },
36194     
36195     hiddenEl : function()
36196     {
36197         return this.el.select('input.hidden-number-input',true).first();
36198     }
36199     
36200 });
36201
36202  
36203
36204 /*
36205 * Licence: LGPL
36206 */
36207
36208 /**
36209  * @class Roo.bootstrap.DocumentSlider
36210  * @extends Roo.bootstrap.Component
36211  * Bootstrap DocumentSlider class
36212  * 
36213  * @constructor
36214  * Create a new DocumentViewer
36215  * @param {Object} config The config object
36216  */
36217
36218 Roo.bootstrap.DocumentSlider = function(config){
36219     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36220     
36221     this.files = [];
36222     
36223     this.addEvents({
36224         /**
36225          * @event initial
36226          * Fire after initEvent
36227          * @param {Roo.bootstrap.DocumentSlider} this
36228          */
36229         "initial" : true,
36230         /**
36231          * @event update
36232          * Fire after update
36233          * @param {Roo.bootstrap.DocumentSlider} this
36234          */
36235         "update" : true,
36236         /**
36237          * @event click
36238          * Fire after click
36239          * @param {Roo.bootstrap.DocumentSlider} this
36240          */
36241         "click" : true
36242     });
36243 };
36244
36245 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36246     
36247     files : false,
36248     
36249     indicator : 0,
36250     
36251     getAutoCreate : function()
36252     {
36253         var cfg = {
36254             tag : 'div',
36255             cls : 'roo-document-slider',
36256             cn : [
36257                 {
36258                     tag : 'div',
36259                     cls : 'roo-document-slider-header',
36260                     cn : [
36261                         {
36262                             tag : 'div',
36263                             cls : 'roo-document-slider-header-title'
36264                         }
36265                     ]
36266                 },
36267                 {
36268                     tag : 'div',
36269                     cls : 'roo-document-slider-body',
36270                     cn : [
36271                         {
36272                             tag : 'div',
36273                             cls : 'roo-document-slider-prev',
36274                             cn : [
36275                                 {
36276                                     tag : 'i',
36277                                     cls : 'fa fa-chevron-left'
36278                                 }
36279                             ]
36280                         },
36281                         {
36282                             tag : 'div',
36283                             cls : 'roo-document-slider-thumb',
36284                             cn : [
36285                                 {
36286                                     tag : 'img',
36287                                     cls : 'roo-document-slider-image'
36288                                 }
36289                             ]
36290                         },
36291                         {
36292                             tag : 'div',
36293                             cls : 'roo-document-slider-next',
36294                             cn : [
36295                                 {
36296                                     tag : 'i',
36297                                     cls : 'fa fa-chevron-right'
36298                                 }
36299                             ]
36300                         }
36301                     ]
36302                 }
36303             ]
36304         };
36305         
36306         return cfg;
36307     },
36308     
36309     initEvents : function()
36310     {
36311         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36312         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36313         
36314         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36315         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36316         
36317         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36318         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36319         
36320         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36321         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36322         
36323         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36324         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36325         
36326         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36327         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36328         
36329         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36330         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36331         
36332         this.thumbEl.on('click', this.onClick, this);
36333         
36334         this.prevIndicator.on('click', this.prev, this);
36335         
36336         this.nextIndicator.on('click', this.next, this);
36337         
36338     },
36339     
36340     initial : function()
36341     {
36342         if(this.files.length){
36343             this.indicator = 1;
36344             this.update()
36345         }
36346         
36347         this.fireEvent('initial', this);
36348     },
36349     
36350     update : function()
36351     {
36352         this.imageEl.attr('src', this.files[this.indicator - 1]);
36353         
36354         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36355         
36356         this.prevIndicator.show();
36357         
36358         if(this.indicator == 1){
36359             this.prevIndicator.hide();
36360         }
36361         
36362         this.nextIndicator.show();
36363         
36364         if(this.indicator == this.files.length){
36365             this.nextIndicator.hide();
36366         }
36367         
36368         this.thumbEl.scrollTo('top');
36369         
36370         this.fireEvent('update', this);
36371     },
36372     
36373     onClick : function(e)
36374     {
36375         e.preventDefault();
36376         
36377         this.fireEvent('click', this);
36378     },
36379     
36380     prev : function(e)
36381     {
36382         e.preventDefault();
36383         
36384         this.indicator = Math.max(1, this.indicator - 1);
36385         
36386         this.update();
36387     },
36388     
36389     next : function(e)
36390     {
36391         e.preventDefault();
36392         
36393         this.indicator = Math.min(this.files.length, this.indicator + 1);
36394         
36395         this.update();
36396     }
36397 });
36398 /*
36399  * - LGPL
36400  *
36401  * RadioSet
36402  *
36403  *
36404  */
36405
36406 /**
36407  * @class Roo.bootstrap.RadioSet
36408  * @extends Roo.bootstrap.Input
36409  * Bootstrap RadioSet class
36410  * @cfg {String} indicatorpos (left|right) default left
36411  * @cfg {Boolean} inline (true|false) inline the element (default true)
36412  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36413  * @constructor
36414  * Create a new RadioSet
36415  * @param {Object} config The config object
36416  */
36417
36418 Roo.bootstrap.RadioSet = function(config){
36419     
36420     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36421     
36422     this.radioes = [];
36423     
36424     Roo.bootstrap.RadioSet.register(this);
36425     
36426     this.addEvents({
36427         /**
36428         * @event check
36429         * Fires when the element is checked or unchecked.
36430         * @param {Roo.bootstrap.RadioSet} this This radio
36431         * @param {Roo.bootstrap.Radio} item The checked item
36432         */
36433        check : true,
36434        /**
36435         * @event click
36436         * Fires when the element is click.
36437         * @param {Roo.bootstrap.RadioSet} this This radio set
36438         * @param {Roo.bootstrap.Radio} item The checked item
36439         * @param {Roo.EventObject} e The event object
36440         */
36441        click : true
36442     });
36443     
36444 };
36445
36446 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36447
36448     radioes : false,
36449     
36450     inline : true,
36451     
36452     weight : '',
36453     
36454     indicatorpos : 'left',
36455     
36456     getAutoCreate : function()
36457     {
36458         var label = {
36459             tag : 'label',
36460             cls : 'roo-radio-set-label',
36461             cn : [
36462                 {
36463                     tag : 'span',
36464                     html : this.fieldLabel
36465                 }
36466             ]
36467         };
36468         if (Roo.bootstrap.version == 3) {
36469             
36470             
36471             if(this.indicatorpos == 'left'){
36472                 label.cn.unshift({
36473                     tag : 'i',
36474                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36475                     tooltip : 'This field is required'
36476                 });
36477             } else {
36478                 label.cn.push({
36479                     tag : 'i',
36480                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36481                     tooltip : 'This field is required'
36482                 });
36483             }
36484         }
36485         var items = {
36486             tag : 'div',
36487             cls : 'roo-radio-set-items'
36488         };
36489         
36490         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36491         
36492         if (align === 'left' && this.fieldLabel.length) {
36493             
36494             items = {
36495                 cls : "roo-radio-set-right", 
36496                 cn: [
36497                     items
36498                 ]
36499             };
36500             
36501             if(this.labelWidth > 12){
36502                 label.style = "width: " + this.labelWidth + 'px';
36503             }
36504             
36505             if(this.labelWidth < 13 && this.labelmd == 0){
36506                 this.labelmd = this.labelWidth;
36507             }
36508             
36509             if(this.labellg > 0){
36510                 label.cls += ' col-lg-' + this.labellg;
36511                 items.cls += ' col-lg-' + (12 - this.labellg);
36512             }
36513             
36514             if(this.labelmd > 0){
36515                 label.cls += ' col-md-' + this.labelmd;
36516                 items.cls += ' col-md-' + (12 - this.labelmd);
36517             }
36518             
36519             if(this.labelsm > 0){
36520                 label.cls += ' col-sm-' + this.labelsm;
36521                 items.cls += ' col-sm-' + (12 - this.labelsm);
36522             }
36523             
36524             if(this.labelxs > 0){
36525                 label.cls += ' col-xs-' + this.labelxs;
36526                 items.cls += ' col-xs-' + (12 - this.labelxs);
36527             }
36528         }
36529         
36530         var cfg = {
36531             tag : 'div',
36532             cls : 'roo-radio-set',
36533             cn : [
36534                 {
36535                     tag : 'input',
36536                     cls : 'roo-radio-set-input',
36537                     type : 'hidden',
36538                     name : this.name,
36539                     value : this.value ? this.value :  ''
36540                 },
36541                 label,
36542                 items
36543             ]
36544         };
36545         
36546         if(this.weight.length){
36547             cfg.cls += ' roo-radio-' + this.weight;
36548         }
36549         
36550         if(this.inline) {
36551             cfg.cls += ' roo-radio-set-inline';
36552         }
36553         
36554         var settings=this;
36555         ['xs','sm','md','lg'].map(function(size){
36556             if (settings[size]) {
36557                 cfg.cls += ' col-' + size + '-' + settings[size];
36558             }
36559         });
36560         
36561         return cfg;
36562         
36563     },
36564
36565     initEvents : function()
36566     {
36567         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36568         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36569         
36570         if(!this.fieldLabel.length){
36571             this.labelEl.hide();
36572         }
36573         
36574         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36575         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36576         
36577         this.indicator = this.indicatorEl();
36578         
36579         if(this.indicator){
36580             this.indicator.addClass('invisible');
36581         }
36582         
36583         this.originalValue = this.getValue();
36584         
36585     },
36586     
36587     inputEl: function ()
36588     {
36589         return this.el.select('.roo-radio-set-input', true).first();
36590     },
36591     
36592     getChildContainer : function()
36593     {
36594         return this.itemsEl;
36595     },
36596     
36597     register : function(item)
36598     {
36599         this.radioes.push(item);
36600         
36601     },
36602     
36603     validate : function()
36604     {   
36605         if(this.getVisibilityEl().hasClass('hidden')){
36606             return true;
36607         }
36608         
36609         var valid = false;
36610         
36611         Roo.each(this.radioes, function(i){
36612             if(!i.checked){
36613                 return;
36614             }
36615             
36616             valid = true;
36617             return false;
36618         });
36619         
36620         if(this.allowBlank) {
36621             return true;
36622         }
36623         
36624         if(this.disabled || valid){
36625             this.markValid();
36626             return true;
36627         }
36628         
36629         this.markInvalid();
36630         return false;
36631         
36632     },
36633     
36634     markValid : function()
36635     {
36636         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36637             this.indicatorEl().removeClass('visible');
36638             this.indicatorEl().addClass('invisible');
36639         }
36640         
36641         
36642         if (Roo.bootstrap.version == 3) {
36643             this.el.removeClass([this.invalidClass, this.validClass]);
36644             this.el.addClass(this.validClass);
36645         } else {
36646             this.el.removeClass(['is-invalid','is-valid']);
36647             this.el.addClass(['is-valid']);
36648         }
36649         this.fireEvent('valid', this);
36650     },
36651     
36652     markInvalid : function(msg)
36653     {
36654         if(this.allowBlank || this.disabled){
36655             return;
36656         }
36657         
36658         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36659             this.indicatorEl().removeClass('invisible');
36660             this.indicatorEl().addClass('visible');
36661         }
36662         if (Roo.bootstrap.version == 3) {
36663             this.el.removeClass([this.invalidClass, this.validClass]);
36664             this.el.addClass(this.invalidClass);
36665         } else {
36666             this.el.removeClass(['is-invalid','is-valid']);
36667             this.el.addClass(['is-invalid']);
36668         }
36669         
36670         this.fireEvent('invalid', this, msg);
36671         
36672     },
36673     
36674     setValue : function(v, suppressEvent)
36675     {   
36676         if(this.value === v){
36677             return;
36678         }
36679         
36680         this.value = v;
36681         
36682         if(this.rendered){
36683             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36684         }
36685         
36686         Roo.each(this.radioes, function(i){
36687             i.checked = false;
36688             i.el.removeClass('checked');
36689         });
36690         
36691         Roo.each(this.radioes, function(i){
36692             
36693             if(i.value === v || i.value.toString() === v.toString()){
36694                 i.checked = true;
36695                 i.el.addClass('checked');
36696                 
36697                 if(suppressEvent !== true){
36698                     this.fireEvent('check', this, i);
36699                 }
36700                 
36701                 return false;
36702             }
36703             
36704         }, this);
36705         
36706         this.validate();
36707     },
36708     
36709     clearInvalid : function(){
36710         
36711         if(!this.el || this.preventMark){
36712             return;
36713         }
36714         
36715         this.el.removeClass([this.invalidClass]);
36716         
36717         this.fireEvent('valid', this);
36718     }
36719     
36720 });
36721
36722 Roo.apply(Roo.bootstrap.RadioSet, {
36723     
36724     groups: {},
36725     
36726     register : function(set)
36727     {
36728         this.groups[set.name] = set;
36729     },
36730     
36731     get: function(name) 
36732     {
36733         if (typeof(this.groups[name]) == 'undefined') {
36734             return false;
36735         }
36736         
36737         return this.groups[name] ;
36738     }
36739     
36740 });
36741 /*
36742  * Based on:
36743  * Ext JS Library 1.1.1
36744  * Copyright(c) 2006-2007, Ext JS, LLC.
36745  *
36746  * Originally Released Under LGPL - original licence link has changed is not relivant.
36747  *
36748  * Fork - LGPL
36749  * <script type="text/javascript">
36750  */
36751
36752
36753 /**
36754  * @class Roo.bootstrap.SplitBar
36755  * @extends Roo.util.Observable
36756  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36757  * <br><br>
36758  * Usage:
36759  * <pre><code>
36760 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36761                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36762 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36763 split.minSize = 100;
36764 split.maxSize = 600;
36765 split.animate = true;
36766 split.on('moved', splitterMoved);
36767 </code></pre>
36768  * @constructor
36769  * Create a new SplitBar
36770  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36771  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36772  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36773  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36774                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36775                         position of the SplitBar).
36776  */
36777 Roo.bootstrap.SplitBar = function(cfg){
36778     
36779     /** @private */
36780     
36781     //{
36782     //  dragElement : elm
36783     //  resizingElement: el,
36784         // optional..
36785     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36786     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36787         // existingProxy ???
36788     //}
36789     
36790     this.el = Roo.get(cfg.dragElement, true);
36791     this.el.dom.unselectable = "on";
36792     /** @private */
36793     this.resizingEl = Roo.get(cfg.resizingElement, true);
36794
36795     /**
36796      * @private
36797      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36798      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36799      * @type Number
36800      */
36801     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36802     
36803     /**
36804      * The minimum size of the resizing element. (Defaults to 0)
36805      * @type Number
36806      */
36807     this.minSize = 0;
36808     
36809     /**
36810      * The maximum size of the resizing element. (Defaults to 2000)
36811      * @type Number
36812      */
36813     this.maxSize = 2000;
36814     
36815     /**
36816      * Whether to animate the transition to the new size
36817      * @type Boolean
36818      */
36819     this.animate = false;
36820     
36821     /**
36822      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36823      * @type Boolean
36824      */
36825     this.useShim = false;
36826     
36827     /** @private */
36828     this.shim = null;
36829     
36830     if(!cfg.existingProxy){
36831         /** @private */
36832         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36833     }else{
36834         this.proxy = Roo.get(cfg.existingProxy).dom;
36835     }
36836     /** @private */
36837     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36838     
36839     /** @private */
36840     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36841     
36842     /** @private */
36843     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36844     
36845     /** @private */
36846     this.dragSpecs = {};
36847     
36848     /**
36849      * @private The adapter to use to positon and resize elements
36850      */
36851     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36852     this.adapter.init(this);
36853     
36854     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36855         /** @private */
36856         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36857         this.el.addClass("roo-splitbar-h");
36858     }else{
36859         /** @private */
36860         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36861         this.el.addClass("roo-splitbar-v");
36862     }
36863     
36864     this.addEvents({
36865         /**
36866          * @event resize
36867          * Fires when the splitter is moved (alias for {@link #event-moved})
36868          * @param {Roo.bootstrap.SplitBar} this
36869          * @param {Number} newSize the new width or height
36870          */
36871         "resize" : true,
36872         /**
36873          * @event moved
36874          * Fires when the splitter is moved
36875          * @param {Roo.bootstrap.SplitBar} this
36876          * @param {Number} newSize the new width or height
36877          */
36878         "moved" : true,
36879         /**
36880          * @event beforeresize
36881          * Fires before the splitter is dragged
36882          * @param {Roo.bootstrap.SplitBar} this
36883          */
36884         "beforeresize" : true,
36885
36886         "beforeapply" : true
36887     });
36888
36889     Roo.util.Observable.call(this);
36890 };
36891
36892 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36893     onStartProxyDrag : function(x, y){
36894         this.fireEvent("beforeresize", this);
36895         if(!this.overlay){
36896             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36897             o.unselectable();
36898             o.enableDisplayMode("block");
36899             // all splitbars share the same overlay
36900             Roo.bootstrap.SplitBar.prototype.overlay = o;
36901         }
36902         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36903         this.overlay.show();
36904         Roo.get(this.proxy).setDisplayed("block");
36905         var size = this.adapter.getElementSize(this);
36906         this.activeMinSize = this.getMinimumSize();;
36907         this.activeMaxSize = this.getMaximumSize();;
36908         var c1 = size - this.activeMinSize;
36909         var c2 = Math.max(this.activeMaxSize - size, 0);
36910         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36911             this.dd.resetConstraints();
36912             this.dd.setXConstraint(
36913                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36914                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36915             );
36916             this.dd.setYConstraint(0, 0);
36917         }else{
36918             this.dd.resetConstraints();
36919             this.dd.setXConstraint(0, 0);
36920             this.dd.setYConstraint(
36921                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36922                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36923             );
36924          }
36925         this.dragSpecs.startSize = size;
36926         this.dragSpecs.startPoint = [x, y];
36927         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36928     },
36929     
36930     /** 
36931      * @private Called after the drag operation by the DDProxy
36932      */
36933     onEndProxyDrag : function(e){
36934         Roo.get(this.proxy).setDisplayed(false);
36935         var endPoint = Roo.lib.Event.getXY(e);
36936         if(this.overlay){
36937             this.overlay.hide();
36938         }
36939         var newSize;
36940         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36941             newSize = this.dragSpecs.startSize + 
36942                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36943                     endPoint[0] - this.dragSpecs.startPoint[0] :
36944                     this.dragSpecs.startPoint[0] - endPoint[0]
36945                 );
36946         }else{
36947             newSize = this.dragSpecs.startSize + 
36948                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36949                     endPoint[1] - this.dragSpecs.startPoint[1] :
36950                     this.dragSpecs.startPoint[1] - endPoint[1]
36951                 );
36952         }
36953         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36954         if(newSize != this.dragSpecs.startSize){
36955             if(this.fireEvent('beforeapply', this, newSize) !== false){
36956                 this.adapter.setElementSize(this, newSize);
36957                 this.fireEvent("moved", this, newSize);
36958                 this.fireEvent("resize", this, newSize);
36959             }
36960         }
36961     },
36962     
36963     /**
36964      * Get the adapter this SplitBar uses
36965      * @return The adapter object
36966      */
36967     getAdapter : function(){
36968         return this.adapter;
36969     },
36970     
36971     /**
36972      * Set the adapter this SplitBar uses
36973      * @param {Object} adapter A SplitBar adapter object
36974      */
36975     setAdapter : function(adapter){
36976         this.adapter = adapter;
36977         this.adapter.init(this);
36978     },
36979     
36980     /**
36981      * Gets the minimum size for the resizing element
36982      * @return {Number} The minimum size
36983      */
36984     getMinimumSize : function(){
36985         return this.minSize;
36986     },
36987     
36988     /**
36989      * Sets the minimum size for the resizing element
36990      * @param {Number} minSize The minimum size
36991      */
36992     setMinimumSize : function(minSize){
36993         this.minSize = minSize;
36994     },
36995     
36996     /**
36997      * Gets the maximum size for the resizing element
36998      * @return {Number} The maximum size
36999      */
37000     getMaximumSize : function(){
37001         return this.maxSize;
37002     },
37003     
37004     /**
37005      * Sets the maximum size for the resizing element
37006      * @param {Number} maxSize The maximum size
37007      */
37008     setMaximumSize : function(maxSize){
37009         this.maxSize = maxSize;
37010     },
37011     
37012     /**
37013      * Sets the initialize size for the resizing element
37014      * @param {Number} size The initial size
37015      */
37016     setCurrentSize : function(size){
37017         var oldAnimate = this.animate;
37018         this.animate = false;
37019         this.adapter.setElementSize(this, size);
37020         this.animate = oldAnimate;
37021     },
37022     
37023     /**
37024      * Destroy this splitbar. 
37025      * @param {Boolean} removeEl True to remove the element
37026      */
37027     destroy : function(removeEl){
37028         if(this.shim){
37029             this.shim.remove();
37030         }
37031         this.dd.unreg();
37032         this.proxy.parentNode.removeChild(this.proxy);
37033         if(removeEl){
37034             this.el.remove();
37035         }
37036     }
37037 });
37038
37039 /**
37040  * @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.
37041  */
37042 Roo.bootstrap.SplitBar.createProxy = function(dir){
37043     var proxy = new Roo.Element(document.createElement("div"));
37044     proxy.unselectable();
37045     var cls = 'roo-splitbar-proxy';
37046     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37047     document.body.appendChild(proxy.dom);
37048     return proxy.dom;
37049 };
37050
37051 /** 
37052  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37053  * Default Adapter. It assumes the splitter and resizing element are not positioned
37054  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37055  */
37056 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37057 };
37058
37059 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37060     // do nothing for now
37061     init : function(s){
37062     
37063     },
37064     /**
37065      * Called before drag operations to get the current size of the resizing element. 
37066      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37067      */
37068      getElementSize : function(s){
37069         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37070             return s.resizingEl.getWidth();
37071         }else{
37072             return s.resizingEl.getHeight();
37073         }
37074     },
37075     
37076     /**
37077      * Called after drag operations to set the size of the resizing element.
37078      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37079      * @param {Number} newSize The new size to set
37080      * @param {Function} onComplete A function to be invoked when resizing is complete
37081      */
37082     setElementSize : function(s, newSize, onComplete){
37083         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37084             if(!s.animate){
37085                 s.resizingEl.setWidth(newSize);
37086                 if(onComplete){
37087                     onComplete(s, newSize);
37088                 }
37089             }else{
37090                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37091             }
37092         }else{
37093             
37094             if(!s.animate){
37095                 s.resizingEl.setHeight(newSize);
37096                 if(onComplete){
37097                     onComplete(s, newSize);
37098                 }
37099             }else{
37100                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37101             }
37102         }
37103     }
37104 };
37105
37106 /** 
37107  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37108  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37109  * Adapter that  moves the splitter element to align with the resized sizing element. 
37110  * Used with an absolute positioned SplitBar.
37111  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37112  * document.body, make sure you assign an id to the body element.
37113  */
37114 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37115     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37116     this.container = Roo.get(container);
37117 };
37118
37119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37120     init : function(s){
37121         this.basic.init(s);
37122     },
37123     
37124     getElementSize : function(s){
37125         return this.basic.getElementSize(s);
37126     },
37127     
37128     setElementSize : function(s, newSize, onComplete){
37129         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37130     },
37131     
37132     moveSplitter : function(s){
37133         var yes = Roo.bootstrap.SplitBar;
37134         switch(s.placement){
37135             case yes.LEFT:
37136                 s.el.setX(s.resizingEl.getRight());
37137                 break;
37138             case yes.RIGHT:
37139                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37140                 break;
37141             case yes.TOP:
37142                 s.el.setY(s.resizingEl.getBottom());
37143                 break;
37144             case yes.BOTTOM:
37145                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37146                 break;
37147         }
37148     }
37149 };
37150
37151 /**
37152  * Orientation constant - Create a vertical SplitBar
37153  * @static
37154  * @type Number
37155  */
37156 Roo.bootstrap.SplitBar.VERTICAL = 1;
37157
37158 /**
37159  * Orientation constant - Create a horizontal SplitBar
37160  * @static
37161  * @type Number
37162  */
37163 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37164
37165 /**
37166  * Placement constant - The resizing element is to the left of the splitter element
37167  * @static
37168  * @type Number
37169  */
37170 Roo.bootstrap.SplitBar.LEFT = 1;
37171
37172 /**
37173  * Placement constant - The resizing element is to the right of the splitter element
37174  * @static
37175  * @type Number
37176  */
37177 Roo.bootstrap.SplitBar.RIGHT = 2;
37178
37179 /**
37180  * Placement constant - The resizing element is positioned above the splitter element
37181  * @static
37182  * @type Number
37183  */
37184 Roo.bootstrap.SplitBar.TOP = 3;
37185
37186 /**
37187  * Placement constant - The resizing element is positioned under splitter element
37188  * @static
37189  * @type Number
37190  */
37191 Roo.bootstrap.SplitBar.BOTTOM = 4;
37192 Roo.namespace("Roo.bootstrap.layout");/*
37193  * Based on:
37194  * Ext JS Library 1.1.1
37195  * Copyright(c) 2006-2007, Ext JS, LLC.
37196  *
37197  * Originally Released Under LGPL - original licence link has changed is not relivant.
37198  *
37199  * Fork - LGPL
37200  * <script type="text/javascript">
37201  */
37202
37203 /**
37204  * @class Roo.bootstrap.layout.Manager
37205  * @extends Roo.bootstrap.Component
37206  * Base class for layout managers.
37207  */
37208 Roo.bootstrap.layout.Manager = function(config)
37209 {
37210     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37211
37212
37213
37214
37215
37216     /** false to disable window resize monitoring @type Boolean */
37217     this.monitorWindowResize = true;
37218     this.regions = {};
37219     this.addEvents({
37220         /**
37221          * @event layout
37222          * Fires when a layout is performed.
37223          * @param {Roo.LayoutManager} this
37224          */
37225         "layout" : true,
37226         /**
37227          * @event regionresized
37228          * Fires when the user resizes a region.
37229          * @param {Roo.LayoutRegion} region The resized region
37230          * @param {Number} newSize The new size (width for east/west, height for north/south)
37231          */
37232         "regionresized" : true,
37233         /**
37234          * @event regioncollapsed
37235          * Fires when a region is collapsed.
37236          * @param {Roo.LayoutRegion} region The collapsed region
37237          */
37238         "regioncollapsed" : true,
37239         /**
37240          * @event regionexpanded
37241          * Fires when a region is expanded.
37242          * @param {Roo.LayoutRegion} region The expanded region
37243          */
37244         "regionexpanded" : true
37245     });
37246     this.updating = false;
37247
37248     if (config.el) {
37249         this.el = Roo.get(config.el);
37250         this.initEvents();
37251     }
37252
37253 };
37254
37255 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37256
37257
37258     regions : null,
37259
37260     monitorWindowResize : true,
37261
37262
37263     updating : false,
37264
37265
37266     onRender : function(ct, position)
37267     {
37268         if(!this.el){
37269             this.el = Roo.get(ct);
37270             this.initEvents();
37271         }
37272         //this.fireEvent('render',this);
37273     },
37274
37275
37276     initEvents: function()
37277     {
37278
37279
37280         // ie scrollbar fix
37281         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37282             document.body.scroll = "no";
37283         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37284             this.el.position('relative');
37285         }
37286         this.id = this.el.id;
37287         this.el.addClass("roo-layout-container");
37288         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37289         if(this.el.dom != document.body ) {
37290             this.el.on('resize', this.layout,this);
37291             this.el.on('show', this.layout,this);
37292         }
37293
37294     },
37295
37296     /**
37297      * Returns true if this layout is currently being updated
37298      * @return {Boolean}
37299      */
37300     isUpdating : function(){
37301         return this.updating;
37302     },
37303
37304     /**
37305      * Suspend the LayoutManager from doing auto-layouts while
37306      * making multiple add or remove calls
37307      */
37308     beginUpdate : function(){
37309         this.updating = true;
37310     },
37311
37312     /**
37313      * Restore auto-layouts and optionally disable the manager from performing a layout
37314      * @param {Boolean} noLayout true to disable a layout update
37315      */
37316     endUpdate : function(noLayout){
37317         this.updating = false;
37318         if(!noLayout){
37319             this.layout();
37320         }
37321     },
37322
37323     layout: function(){
37324         // abstract...
37325     },
37326
37327     onRegionResized : function(region, newSize){
37328         this.fireEvent("regionresized", region, newSize);
37329         this.layout();
37330     },
37331
37332     onRegionCollapsed : function(region){
37333         this.fireEvent("regioncollapsed", region);
37334     },
37335
37336     onRegionExpanded : function(region){
37337         this.fireEvent("regionexpanded", region);
37338     },
37339
37340     /**
37341      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37342      * performs box-model adjustments.
37343      * @return {Object} The size as an object {width: (the width), height: (the height)}
37344      */
37345     getViewSize : function()
37346     {
37347         var size;
37348         if(this.el.dom != document.body){
37349             size = this.el.getSize();
37350         }else{
37351             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37352         }
37353         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37354         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37355         return size;
37356     },
37357
37358     /**
37359      * Returns the Element this layout is bound to.
37360      * @return {Roo.Element}
37361      */
37362     getEl : function(){
37363         return this.el;
37364     },
37365
37366     /**
37367      * Returns the specified region.
37368      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37369      * @return {Roo.LayoutRegion}
37370      */
37371     getRegion : function(target){
37372         return this.regions[target.toLowerCase()];
37373     },
37374
37375     onWindowResize : function(){
37376         if(this.monitorWindowResize){
37377             this.layout();
37378         }
37379     }
37380 });
37381 /*
37382  * Based on:
37383  * Ext JS Library 1.1.1
37384  * Copyright(c) 2006-2007, Ext JS, LLC.
37385  *
37386  * Originally Released Under LGPL - original licence link has changed is not relivant.
37387  *
37388  * Fork - LGPL
37389  * <script type="text/javascript">
37390  */
37391 /**
37392  * @class Roo.bootstrap.layout.Border
37393  * @extends Roo.bootstrap.layout.Manager
37394  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37395  * please see: examples/bootstrap/nested.html<br><br>
37396  
37397 <b>The container the layout is rendered into can be either the body element or any other element.
37398 If it is not the body element, the container needs to either be an absolute positioned element,
37399 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37400 the container size if it is not the body element.</b>
37401
37402 * @constructor
37403 * Create a new Border
37404 * @param {Object} config Configuration options
37405  */
37406 Roo.bootstrap.layout.Border = function(config){
37407     config = config || {};
37408     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37409     
37410     
37411     
37412     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37413         if(config[region]){
37414             config[region].region = region;
37415             this.addRegion(config[region]);
37416         }
37417     },this);
37418     
37419 };
37420
37421 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37422
37423 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37424     
37425     parent : false, // this might point to a 'nest' or a ???
37426     
37427     /**
37428      * Creates and adds a new region if it doesn't already exist.
37429      * @param {String} target The target region key (north, south, east, west or center).
37430      * @param {Object} config The regions config object
37431      * @return {BorderLayoutRegion} The new region
37432      */
37433     addRegion : function(config)
37434     {
37435         if(!this.regions[config.region]){
37436             var r = this.factory(config);
37437             this.bindRegion(r);
37438         }
37439         return this.regions[config.region];
37440     },
37441
37442     // private (kinda)
37443     bindRegion : function(r){
37444         this.regions[r.config.region] = r;
37445         
37446         r.on("visibilitychange",    this.layout, this);
37447         r.on("paneladded",          this.layout, this);
37448         r.on("panelremoved",        this.layout, this);
37449         r.on("invalidated",         this.layout, this);
37450         r.on("resized",             this.onRegionResized, this);
37451         r.on("collapsed",           this.onRegionCollapsed, this);
37452         r.on("expanded",            this.onRegionExpanded, this);
37453     },
37454
37455     /**
37456      * Performs a layout update.
37457      */
37458     layout : function()
37459     {
37460         if(this.updating) {
37461             return;
37462         }
37463         
37464         // render all the rebions if they have not been done alreayd?
37465         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37466             if(this.regions[region] && !this.regions[region].bodyEl){
37467                 this.regions[region].onRender(this.el)
37468             }
37469         },this);
37470         
37471         var size = this.getViewSize();
37472         var w = size.width;
37473         var h = size.height;
37474         var centerW = w;
37475         var centerH = h;
37476         var centerY = 0;
37477         var centerX = 0;
37478         //var x = 0, y = 0;
37479
37480         var rs = this.regions;
37481         var north = rs["north"];
37482         var south = rs["south"]; 
37483         var west = rs["west"];
37484         var east = rs["east"];
37485         var center = rs["center"];
37486         //if(this.hideOnLayout){ // not supported anymore
37487             //c.el.setStyle("display", "none");
37488         //}
37489         if(north && north.isVisible()){
37490             var b = north.getBox();
37491             var m = north.getMargins();
37492             b.width = w - (m.left+m.right);
37493             b.x = m.left;
37494             b.y = m.top;
37495             centerY = b.height + b.y + m.bottom;
37496             centerH -= centerY;
37497             north.updateBox(this.safeBox(b));
37498         }
37499         if(south && south.isVisible()){
37500             var b = south.getBox();
37501             var m = south.getMargins();
37502             b.width = w - (m.left+m.right);
37503             b.x = m.left;
37504             var totalHeight = (b.height + m.top + m.bottom);
37505             b.y = h - totalHeight + m.top;
37506             centerH -= totalHeight;
37507             south.updateBox(this.safeBox(b));
37508         }
37509         if(west && west.isVisible()){
37510             var b = west.getBox();
37511             var m = west.getMargins();
37512             b.height = centerH - (m.top+m.bottom);
37513             b.x = m.left;
37514             b.y = centerY + m.top;
37515             var totalWidth = (b.width + m.left + m.right);
37516             centerX += totalWidth;
37517             centerW -= totalWidth;
37518             west.updateBox(this.safeBox(b));
37519         }
37520         if(east && east.isVisible()){
37521             var b = east.getBox();
37522             var m = east.getMargins();
37523             b.height = centerH - (m.top+m.bottom);
37524             var totalWidth = (b.width + m.left + m.right);
37525             b.x = w - totalWidth + m.left;
37526             b.y = centerY + m.top;
37527             centerW -= totalWidth;
37528             east.updateBox(this.safeBox(b));
37529         }
37530         if(center){
37531             var m = center.getMargins();
37532             var centerBox = {
37533                 x: centerX + m.left,
37534                 y: centerY + m.top,
37535                 width: centerW - (m.left+m.right),
37536                 height: centerH - (m.top+m.bottom)
37537             };
37538             //if(this.hideOnLayout){
37539                 //center.el.setStyle("display", "block");
37540             //}
37541             center.updateBox(this.safeBox(centerBox));
37542         }
37543         this.el.repaint();
37544         this.fireEvent("layout", this);
37545     },
37546
37547     // private
37548     safeBox : function(box){
37549         box.width = Math.max(0, box.width);
37550         box.height = Math.max(0, box.height);
37551         return box;
37552     },
37553
37554     /**
37555      * Adds a ContentPanel (or subclass) to this layout.
37556      * @param {String} target The target region key (north, south, east, west or center).
37557      * @param {Roo.ContentPanel} panel The panel to add
37558      * @return {Roo.ContentPanel} The added panel
37559      */
37560     add : function(target, panel){
37561          
37562         target = target.toLowerCase();
37563         return this.regions[target].add(panel);
37564     },
37565
37566     /**
37567      * Remove a ContentPanel (or subclass) to this layout.
37568      * @param {String} target The target region key (north, south, east, west or center).
37569      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37570      * @return {Roo.ContentPanel} The removed panel
37571      */
37572     remove : function(target, panel){
37573         target = target.toLowerCase();
37574         return this.regions[target].remove(panel);
37575     },
37576
37577     /**
37578      * Searches all regions for a panel with the specified id
37579      * @param {String} panelId
37580      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37581      */
37582     findPanel : function(panelId){
37583         var rs = this.regions;
37584         for(var target in rs){
37585             if(typeof rs[target] != "function"){
37586                 var p = rs[target].getPanel(panelId);
37587                 if(p){
37588                     return p;
37589                 }
37590             }
37591         }
37592         return null;
37593     },
37594
37595     /**
37596      * Searches all regions for a panel with the specified id and activates (shows) it.
37597      * @param {String/ContentPanel} panelId The panels id or the panel itself
37598      * @return {Roo.ContentPanel} The shown panel or null
37599      */
37600     showPanel : function(panelId) {
37601       var rs = this.regions;
37602       for(var target in rs){
37603          var r = rs[target];
37604          if(typeof r != "function"){
37605             if(r.hasPanel(panelId)){
37606                return r.showPanel(panelId);
37607             }
37608          }
37609       }
37610       return null;
37611    },
37612
37613    /**
37614      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37615      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37616      */
37617    /*
37618     restoreState : function(provider){
37619         if(!provider){
37620             provider = Roo.state.Manager;
37621         }
37622         var sm = new Roo.LayoutStateManager();
37623         sm.init(this, provider);
37624     },
37625 */
37626  
37627  
37628     /**
37629      * Adds a xtype elements to the layout.
37630      * <pre><code>
37631
37632 layout.addxtype({
37633        xtype : 'ContentPanel',
37634        region: 'west',
37635        items: [ .... ]
37636    }
37637 );
37638
37639 layout.addxtype({
37640         xtype : 'NestedLayoutPanel',
37641         region: 'west',
37642         layout: {
37643            center: { },
37644            west: { }   
37645         },
37646         items : [ ... list of content panels or nested layout panels.. ]
37647    }
37648 );
37649 </code></pre>
37650      * @param {Object} cfg Xtype definition of item to add.
37651      */
37652     addxtype : function(cfg)
37653     {
37654         // basically accepts a pannel...
37655         // can accept a layout region..!?!?
37656         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37657         
37658         
37659         // theory?  children can only be panels??
37660         
37661         //if (!cfg.xtype.match(/Panel$/)) {
37662         //    return false;
37663         //}
37664         var ret = false;
37665         
37666         if (typeof(cfg.region) == 'undefined') {
37667             Roo.log("Failed to add Panel, region was not set");
37668             Roo.log(cfg);
37669             return false;
37670         }
37671         var region = cfg.region;
37672         delete cfg.region;
37673         
37674           
37675         var xitems = [];
37676         if (cfg.items) {
37677             xitems = cfg.items;
37678             delete cfg.items;
37679         }
37680         var nb = false;
37681         
37682         if ( region == 'center') {
37683             Roo.log("Center: " + cfg.title);
37684         }
37685         
37686         
37687         switch(cfg.xtype) 
37688         {
37689             case 'Content':  // ContentPanel (el, cfg)
37690             case 'Scroll':  // ContentPanel (el, cfg)
37691             case 'View': 
37692                 cfg.autoCreate = cfg.autoCreate || true;
37693                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37694                 //} else {
37695                 //    var el = this.el.createChild();
37696                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37697                 //}
37698                 
37699                 this.add(region, ret);
37700                 break;
37701             
37702             /*
37703             case 'TreePanel': // our new panel!
37704                 cfg.el = this.el.createChild();
37705                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37706                 this.add(region, ret);
37707                 break;
37708             */
37709             
37710             case 'Nest': 
37711                 // create a new Layout (which is  a Border Layout...
37712                 
37713                 var clayout = cfg.layout;
37714                 clayout.el  = this.el.createChild();
37715                 clayout.items   = clayout.items  || [];
37716                 
37717                 delete cfg.layout;
37718                 
37719                 // replace this exitems with the clayout ones..
37720                 xitems = clayout.items;
37721                  
37722                 // force background off if it's in center...
37723                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37724                     cfg.background = false;
37725                 }
37726                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37727                 
37728                 
37729                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37730                 //console.log('adding nested layout panel '  + cfg.toSource());
37731                 this.add(region, ret);
37732                 nb = {}; /// find first...
37733                 break;
37734             
37735             case 'Grid':
37736                 
37737                 // needs grid and region
37738                 
37739                 //var el = this.getRegion(region).el.createChild();
37740                 /*
37741                  *var el = this.el.createChild();
37742                 // create the grid first...
37743                 cfg.grid.container = el;
37744                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37745                 */
37746                 
37747                 if (region == 'center' && this.active ) {
37748                     cfg.background = false;
37749                 }
37750                 
37751                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37752                 
37753                 this.add(region, ret);
37754                 /*
37755                 if (cfg.background) {
37756                     // render grid on panel activation (if panel background)
37757                     ret.on('activate', function(gp) {
37758                         if (!gp.grid.rendered) {
37759                     //        gp.grid.render(el);
37760                         }
37761                     });
37762                 } else {
37763                   //  cfg.grid.render(el);
37764                 }
37765                 */
37766                 break;
37767            
37768            
37769             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37770                 // it was the old xcomponent building that caused this before.
37771                 // espeically if border is the top element in the tree.
37772                 ret = this;
37773                 break; 
37774                 
37775                     
37776                 
37777                 
37778                 
37779             default:
37780                 /*
37781                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37782                     
37783                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37784                     this.add(region, ret);
37785                 } else {
37786                 */
37787                     Roo.log(cfg);
37788                     throw "Can not add '" + cfg.xtype + "' to Border";
37789                     return null;
37790              
37791                                 
37792              
37793         }
37794         this.beginUpdate();
37795         // add children..
37796         var region = '';
37797         var abn = {};
37798         Roo.each(xitems, function(i)  {
37799             region = nb && i.region ? i.region : false;
37800             
37801             var add = ret.addxtype(i);
37802            
37803             if (region) {
37804                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37805                 if (!i.background) {
37806                     abn[region] = nb[region] ;
37807                 }
37808             }
37809             
37810         });
37811         this.endUpdate();
37812
37813         // make the last non-background panel active..
37814         //if (nb) { Roo.log(abn); }
37815         if (nb) {
37816             
37817             for(var r in abn) {
37818                 region = this.getRegion(r);
37819                 if (region) {
37820                     // tried using nb[r], but it does not work..
37821                      
37822                     region.showPanel(abn[r]);
37823                    
37824                 }
37825             }
37826         }
37827         return ret;
37828         
37829     },
37830     
37831     
37832 // private
37833     factory : function(cfg)
37834     {
37835         
37836         var validRegions = Roo.bootstrap.layout.Border.regions;
37837
37838         var target = cfg.region;
37839         cfg.mgr = this;
37840         
37841         var r = Roo.bootstrap.layout;
37842         Roo.log(target);
37843         switch(target){
37844             case "north":
37845                 return new r.North(cfg);
37846             case "south":
37847                 return new r.South(cfg);
37848             case "east":
37849                 return new r.East(cfg);
37850             case "west":
37851                 return new r.West(cfg);
37852             case "center":
37853                 return new r.Center(cfg);
37854         }
37855         throw 'Layout region "'+target+'" not supported.';
37856     }
37857     
37858     
37859 });
37860  /*
37861  * Based on:
37862  * Ext JS Library 1.1.1
37863  * Copyright(c) 2006-2007, Ext JS, LLC.
37864  *
37865  * Originally Released Under LGPL - original licence link has changed is not relivant.
37866  *
37867  * Fork - LGPL
37868  * <script type="text/javascript">
37869  */
37870  
37871 /**
37872  * @class Roo.bootstrap.layout.Basic
37873  * @extends Roo.util.Observable
37874  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37875  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37876  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37877  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37878  * @cfg {string}   region  the region that it inhabits..
37879  * @cfg {bool}   skipConfig skip config?
37880  * 
37881
37882  */
37883 Roo.bootstrap.layout.Basic = function(config){
37884     
37885     this.mgr = config.mgr;
37886     
37887     this.position = config.region;
37888     
37889     var skipConfig = config.skipConfig;
37890     
37891     this.events = {
37892         /**
37893          * @scope Roo.BasicLayoutRegion
37894          */
37895         
37896         /**
37897          * @event beforeremove
37898          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37899          * @param {Roo.LayoutRegion} this
37900          * @param {Roo.ContentPanel} panel The panel
37901          * @param {Object} e The cancel event object
37902          */
37903         "beforeremove" : true,
37904         /**
37905          * @event invalidated
37906          * Fires when the layout for this region is changed.
37907          * @param {Roo.LayoutRegion} this
37908          */
37909         "invalidated" : true,
37910         /**
37911          * @event visibilitychange
37912          * Fires when this region is shown or hidden 
37913          * @param {Roo.LayoutRegion} this
37914          * @param {Boolean} visibility true or false
37915          */
37916         "visibilitychange" : true,
37917         /**
37918          * @event paneladded
37919          * Fires when a panel is added. 
37920          * @param {Roo.LayoutRegion} this
37921          * @param {Roo.ContentPanel} panel The panel
37922          */
37923         "paneladded" : true,
37924         /**
37925          * @event panelremoved
37926          * Fires when a panel is removed. 
37927          * @param {Roo.LayoutRegion} this
37928          * @param {Roo.ContentPanel} panel The panel
37929          */
37930         "panelremoved" : true,
37931         /**
37932          * @event beforecollapse
37933          * Fires when this region before collapse.
37934          * @param {Roo.LayoutRegion} this
37935          */
37936         "beforecollapse" : true,
37937         /**
37938          * @event collapsed
37939          * Fires when this region is collapsed.
37940          * @param {Roo.LayoutRegion} this
37941          */
37942         "collapsed" : true,
37943         /**
37944          * @event expanded
37945          * Fires when this region is expanded.
37946          * @param {Roo.LayoutRegion} this
37947          */
37948         "expanded" : true,
37949         /**
37950          * @event slideshow
37951          * Fires when this region is slid into view.
37952          * @param {Roo.LayoutRegion} this
37953          */
37954         "slideshow" : true,
37955         /**
37956          * @event slidehide
37957          * Fires when this region slides out of view. 
37958          * @param {Roo.LayoutRegion} this
37959          */
37960         "slidehide" : true,
37961         /**
37962          * @event panelactivated
37963          * Fires when a panel is activated. 
37964          * @param {Roo.LayoutRegion} this
37965          * @param {Roo.ContentPanel} panel The activated panel
37966          */
37967         "panelactivated" : true,
37968         /**
37969          * @event resized
37970          * Fires when the user resizes this region. 
37971          * @param {Roo.LayoutRegion} this
37972          * @param {Number} newSize The new size (width for east/west, height for north/south)
37973          */
37974         "resized" : true
37975     };
37976     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37977     this.panels = new Roo.util.MixedCollection();
37978     this.panels.getKey = this.getPanelId.createDelegate(this);
37979     this.box = null;
37980     this.activePanel = null;
37981     // ensure listeners are added...
37982     
37983     if (config.listeners || config.events) {
37984         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37985             listeners : config.listeners || {},
37986             events : config.events || {}
37987         });
37988     }
37989     
37990     if(skipConfig !== true){
37991         this.applyConfig(config);
37992     }
37993 };
37994
37995 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37996 {
37997     getPanelId : function(p){
37998         return p.getId();
37999     },
38000     
38001     applyConfig : function(config){
38002         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38003         this.config = config;
38004         
38005     },
38006     
38007     /**
38008      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38009      * the width, for horizontal (north, south) the height.
38010      * @param {Number} newSize The new width or height
38011      */
38012     resizeTo : function(newSize){
38013         var el = this.el ? this.el :
38014                  (this.activePanel ? this.activePanel.getEl() : null);
38015         if(el){
38016             switch(this.position){
38017                 case "east":
38018                 case "west":
38019                     el.setWidth(newSize);
38020                     this.fireEvent("resized", this, newSize);
38021                 break;
38022                 case "north":
38023                 case "south":
38024                     el.setHeight(newSize);
38025                     this.fireEvent("resized", this, newSize);
38026                 break;                
38027             }
38028         }
38029     },
38030     
38031     getBox : function(){
38032         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38033     },
38034     
38035     getMargins : function(){
38036         return this.margins;
38037     },
38038     
38039     updateBox : function(box){
38040         this.box = box;
38041         var el = this.activePanel.getEl();
38042         el.dom.style.left = box.x + "px";
38043         el.dom.style.top = box.y + "px";
38044         this.activePanel.setSize(box.width, box.height);
38045     },
38046     
38047     /**
38048      * Returns the container element for this region.
38049      * @return {Roo.Element}
38050      */
38051     getEl : function(){
38052         return this.activePanel;
38053     },
38054     
38055     /**
38056      * Returns true if this region is currently visible.
38057      * @return {Boolean}
38058      */
38059     isVisible : function(){
38060         return this.activePanel ? true : false;
38061     },
38062     
38063     setActivePanel : function(panel){
38064         panel = this.getPanel(panel);
38065         if(this.activePanel && this.activePanel != panel){
38066             this.activePanel.setActiveState(false);
38067             this.activePanel.getEl().setLeftTop(-10000,-10000);
38068         }
38069         this.activePanel = panel;
38070         panel.setActiveState(true);
38071         if(this.box){
38072             panel.setSize(this.box.width, this.box.height);
38073         }
38074         this.fireEvent("panelactivated", this, panel);
38075         this.fireEvent("invalidated");
38076     },
38077     
38078     /**
38079      * Show the specified panel.
38080      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38081      * @return {Roo.ContentPanel} The shown panel or null
38082      */
38083     showPanel : function(panel){
38084         panel = this.getPanel(panel);
38085         if(panel){
38086             this.setActivePanel(panel);
38087         }
38088         return panel;
38089     },
38090     
38091     /**
38092      * Get the active panel for this region.
38093      * @return {Roo.ContentPanel} The active panel or null
38094      */
38095     getActivePanel : function(){
38096         return this.activePanel;
38097     },
38098     
38099     /**
38100      * Add the passed ContentPanel(s)
38101      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38102      * @return {Roo.ContentPanel} The panel added (if only one was added)
38103      */
38104     add : function(panel){
38105         if(arguments.length > 1){
38106             for(var i = 0, len = arguments.length; i < len; i++) {
38107                 this.add(arguments[i]);
38108             }
38109             return null;
38110         }
38111         if(this.hasPanel(panel)){
38112             this.showPanel(panel);
38113             return panel;
38114         }
38115         var el = panel.getEl();
38116         if(el.dom.parentNode != this.mgr.el.dom){
38117             this.mgr.el.dom.appendChild(el.dom);
38118         }
38119         if(panel.setRegion){
38120             panel.setRegion(this);
38121         }
38122         this.panels.add(panel);
38123         el.setStyle("position", "absolute");
38124         if(!panel.background){
38125             this.setActivePanel(panel);
38126             if(this.config.initialSize && this.panels.getCount()==1){
38127                 this.resizeTo(this.config.initialSize);
38128             }
38129         }
38130         this.fireEvent("paneladded", this, panel);
38131         return panel;
38132     },
38133     
38134     /**
38135      * Returns true if the panel is in this region.
38136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38137      * @return {Boolean}
38138      */
38139     hasPanel : function(panel){
38140         if(typeof panel == "object"){ // must be panel obj
38141             panel = panel.getId();
38142         }
38143         return this.getPanel(panel) ? true : false;
38144     },
38145     
38146     /**
38147      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38148      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38149      * @param {Boolean} preservePanel Overrides the config preservePanel option
38150      * @return {Roo.ContentPanel} The panel that was removed
38151      */
38152     remove : function(panel, preservePanel){
38153         panel = this.getPanel(panel);
38154         if(!panel){
38155             return null;
38156         }
38157         var e = {};
38158         this.fireEvent("beforeremove", this, panel, e);
38159         if(e.cancel === true){
38160             return null;
38161         }
38162         var panelId = panel.getId();
38163         this.panels.removeKey(panelId);
38164         return panel;
38165     },
38166     
38167     /**
38168      * Returns the panel specified or null if it's not in this region.
38169      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38170      * @return {Roo.ContentPanel}
38171      */
38172     getPanel : function(id){
38173         if(typeof id == "object"){ // must be panel obj
38174             return id;
38175         }
38176         return this.panels.get(id);
38177     },
38178     
38179     /**
38180      * Returns this regions position (north/south/east/west/center).
38181      * @return {String} 
38182      */
38183     getPosition: function(){
38184         return this.position;    
38185     }
38186 });/*
38187  * Based on:
38188  * Ext JS Library 1.1.1
38189  * Copyright(c) 2006-2007, Ext JS, LLC.
38190  *
38191  * Originally Released Under LGPL - original licence link has changed is not relivant.
38192  *
38193  * Fork - LGPL
38194  * <script type="text/javascript">
38195  */
38196  
38197 /**
38198  * @class Roo.bootstrap.layout.Region
38199  * @extends Roo.bootstrap.layout.Basic
38200  * This class represents a region in a layout manager.
38201  
38202  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38203  * @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})
38204  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38205  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38206  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38207  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38208  * @cfg {String}    title           The title for the region (overrides panel titles)
38209  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38210  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38211  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38212  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38213  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38214  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38215  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38216  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38217  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38218  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38219
38220  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38221  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38222  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38223  * @cfg {Number}    width           For East/West panels
38224  * @cfg {Number}    height          For North/South panels
38225  * @cfg {Boolean}   split           To show the splitter
38226  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38227  * 
38228  * @cfg {string}   cls             Extra CSS classes to add to region
38229  * 
38230  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38231  * @cfg {string}   region  the region that it inhabits..
38232  *
38233
38234  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38235  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38236
38237  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38238  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38239  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38240  */
38241 Roo.bootstrap.layout.Region = function(config)
38242 {
38243     this.applyConfig(config);
38244
38245     var mgr = config.mgr;
38246     var pos = config.region;
38247     config.skipConfig = true;
38248     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38249     
38250     if (mgr.el) {
38251         this.onRender(mgr.el);   
38252     }
38253      
38254     this.visible = true;
38255     this.collapsed = false;
38256     this.unrendered_panels = [];
38257 };
38258
38259 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38260
38261     position: '', // set by wrapper (eg. north/south etc..)
38262     unrendered_panels : null,  // unrendered panels.
38263     
38264     tabPosition : false,
38265     
38266     mgr: false, // points to 'Border'
38267     
38268     
38269     createBody : function(){
38270         /** This region's body element 
38271         * @type Roo.Element */
38272         this.bodyEl = this.el.createChild({
38273                 tag: "div",
38274                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38275         });
38276     },
38277
38278     onRender: function(ctr, pos)
38279     {
38280         var dh = Roo.DomHelper;
38281         /** This region's container element 
38282         * @type Roo.Element */
38283         this.el = dh.append(ctr.dom, {
38284                 tag: "div",
38285                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38286             }, true);
38287         /** This region's title element 
38288         * @type Roo.Element */
38289     
38290         this.titleEl = dh.append(this.el.dom,  {
38291                 tag: "div",
38292                 unselectable: "on",
38293                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38294                 children:[
38295                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38296                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38297                 ]
38298             }, true);
38299         
38300         this.titleEl.enableDisplayMode();
38301         /** This region's title text element 
38302         * @type HTMLElement */
38303         this.titleTextEl = this.titleEl.dom.firstChild;
38304         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38305         /*
38306         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38307         this.closeBtn.enableDisplayMode();
38308         this.closeBtn.on("click", this.closeClicked, this);
38309         this.closeBtn.hide();
38310     */
38311         this.createBody(this.config);
38312         if(this.config.hideWhenEmpty){
38313             this.hide();
38314             this.on("paneladded", this.validateVisibility, this);
38315             this.on("panelremoved", this.validateVisibility, this);
38316         }
38317         if(this.autoScroll){
38318             this.bodyEl.setStyle("overflow", "auto");
38319         }else{
38320             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38321         }
38322         //if(c.titlebar !== false){
38323             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38324                 this.titleEl.hide();
38325             }else{
38326                 this.titleEl.show();
38327                 if(this.config.title){
38328                     this.titleTextEl.innerHTML = this.config.title;
38329                 }
38330             }
38331         //}
38332         if(this.config.collapsed){
38333             this.collapse(true);
38334         }
38335         if(this.config.hidden){
38336             this.hide();
38337         }
38338         
38339         if (this.unrendered_panels && this.unrendered_panels.length) {
38340             for (var i =0;i< this.unrendered_panels.length; i++) {
38341                 this.add(this.unrendered_panels[i]);
38342             }
38343             this.unrendered_panels = null;
38344             
38345         }
38346         
38347     },
38348     
38349     applyConfig : function(c)
38350     {
38351         /*
38352          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38353             var dh = Roo.DomHelper;
38354             if(c.titlebar !== false){
38355                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38356                 this.collapseBtn.on("click", this.collapse, this);
38357                 this.collapseBtn.enableDisplayMode();
38358                 /*
38359                 if(c.showPin === true || this.showPin){
38360                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38361                     this.stickBtn.enableDisplayMode();
38362                     this.stickBtn.on("click", this.expand, this);
38363                     this.stickBtn.hide();
38364                 }
38365                 
38366             }
38367             */
38368             /** This region's collapsed element
38369             * @type Roo.Element */
38370             /*
38371              *
38372             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38373                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38374             ]}, true);
38375             
38376             if(c.floatable !== false){
38377                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38378                this.collapsedEl.on("click", this.collapseClick, this);
38379             }
38380
38381             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38382                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38383                    id: "message", unselectable: "on", style:{"float":"left"}});
38384                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38385              }
38386             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38387             this.expandBtn.on("click", this.expand, this);
38388             
38389         }
38390         
38391         if(this.collapseBtn){
38392             this.collapseBtn.setVisible(c.collapsible == true);
38393         }
38394         
38395         this.cmargins = c.cmargins || this.cmargins ||
38396                          (this.position == "west" || this.position == "east" ?
38397                              {top: 0, left: 2, right:2, bottom: 0} :
38398                              {top: 2, left: 0, right:0, bottom: 2});
38399         */
38400         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38401         
38402         
38403         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38404         
38405         this.autoScroll = c.autoScroll || false;
38406         
38407         
38408        
38409         
38410         this.duration = c.duration || .30;
38411         this.slideDuration = c.slideDuration || .45;
38412         this.config = c;
38413        
38414     },
38415     /**
38416      * Returns true if this region is currently visible.
38417      * @return {Boolean}
38418      */
38419     isVisible : function(){
38420         return this.visible;
38421     },
38422
38423     /**
38424      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38425      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38426      */
38427     //setCollapsedTitle : function(title){
38428     //    title = title || "&#160;";
38429      //   if(this.collapsedTitleTextEl){
38430       //      this.collapsedTitleTextEl.innerHTML = title;
38431        // }
38432     //},
38433
38434     getBox : function(){
38435         var b;
38436       //  if(!this.collapsed){
38437             b = this.el.getBox(false, true);
38438        // }else{
38439           //  b = this.collapsedEl.getBox(false, true);
38440         //}
38441         return b;
38442     },
38443
38444     getMargins : function(){
38445         return this.margins;
38446         //return this.collapsed ? this.cmargins : this.margins;
38447     },
38448 /*
38449     highlight : function(){
38450         this.el.addClass("x-layout-panel-dragover");
38451     },
38452
38453     unhighlight : function(){
38454         this.el.removeClass("x-layout-panel-dragover");
38455     },
38456 */
38457     updateBox : function(box)
38458     {
38459         if (!this.bodyEl) {
38460             return; // not rendered yet..
38461         }
38462         
38463         this.box = box;
38464         if(!this.collapsed){
38465             this.el.dom.style.left = box.x + "px";
38466             this.el.dom.style.top = box.y + "px";
38467             this.updateBody(box.width, box.height);
38468         }else{
38469             this.collapsedEl.dom.style.left = box.x + "px";
38470             this.collapsedEl.dom.style.top = box.y + "px";
38471             this.collapsedEl.setSize(box.width, box.height);
38472         }
38473         if(this.tabs){
38474             this.tabs.autoSizeTabs();
38475         }
38476     },
38477
38478     updateBody : function(w, h)
38479     {
38480         if(w !== null){
38481             this.el.setWidth(w);
38482             w -= this.el.getBorderWidth("rl");
38483             if(this.config.adjustments){
38484                 w += this.config.adjustments[0];
38485             }
38486         }
38487         if(h !== null && h > 0){
38488             this.el.setHeight(h);
38489             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38490             h -= this.el.getBorderWidth("tb");
38491             if(this.config.adjustments){
38492                 h += this.config.adjustments[1];
38493             }
38494             this.bodyEl.setHeight(h);
38495             if(this.tabs){
38496                 h = this.tabs.syncHeight(h);
38497             }
38498         }
38499         if(this.panelSize){
38500             w = w !== null ? w : this.panelSize.width;
38501             h = h !== null ? h : this.panelSize.height;
38502         }
38503         if(this.activePanel){
38504             var el = this.activePanel.getEl();
38505             w = w !== null ? w : el.getWidth();
38506             h = h !== null ? h : el.getHeight();
38507             this.panelSize = {width: w, height: h};
38508             this.activePanel.setSize(w, h);
38509         }
38510         if(Roo.isIE && this.tabs){
38511             this.tabs.el.repaint();
38512         }
38513     },
38514
38515     /**
38516      * Returns the container element for this region.
38517      * @return {Roo.Element}
38518      */
38519     getEl : function(){
38520         return this.el;
38521     },
38522
38523     /**
38524      * Hides this region.
38525      */
38526     hide : function(){
38527         //if(!this.collapsed){
38528             this.el.dom.style.left = "-2000px";
38529             this.el.hide();
38530         //}else{
38531          //   this.collapsedEl.dom.style.left = "-2000px";
38532          //   this.collapsedEl.hide();
38533        // }
38534         this.visible = false;
38535         this.fireEvent("visibilitychange", this, false);
38536     },
38537
38538     /**
38539      * Shows this region if it was previously hidden.
38540      */
38541     show : function(){
38542         //if(!this.collapsed){
38543             this.el.show();
38544         //}else{
38545         //    this.collapsedEl.show();
38546        // }
38547         this.visible = true;
38548         this.fireEvent("visibilitychange", this, true);
38549     },
38550 /*
38551     closeClicked : function(){
38552         if(this.activePanel){
38553             this.remove(this.activePanel);
38554         }
38555     },
38556
38557     collapseClick : function(e){
38558         if(this.isSlid){
38559            e.stopPropagation();
38560            this.slideIn();
38561         }else{
38562            e.stopPropagation();
38563            this.slideOut();
38564         }
38565     },
38566 */
38567     /**
38568      * Collapses this region.
38569      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38570      */
38571     /*
38572     collapse : function(skipAnim, skipCheck = false){
38573         if(this.collapsed) {
38574             return;
38575         }
38576         
38577         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38578             
38579             this.collapsed = true;
38580             if(this.split){
38581                 this.split.el.hide();
38582             }
38583             if(this.config.animate && skipAnim !== true){
38584                 this.fireEvent("invalidated", this);
38585                 this.animateCollapse();
38586             }else{
38587                 this.el.setLocation(-20000,-20000);
38588                 this.el.hide();
38589                 this.collapsedEl.show();
38590                 this.fireEvent("collapsed", this);
38591                 this.fireEvent("invalidated", this);
38592             }
38593         }
38594         
38595     },
38596 */
38597     animateCollapse : function(){
38598         // overridden
38599     },
38600
38601     /**
38602      * Expands this region if it was previously collapsed.
38603      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38604      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38605      */
38606     /*
38607     expand : function(e, skipAnim){
38608         if(e) {
38609             e.stopPropagation();
38610         }
38611         if(!this.collapsed || this.el.hasActiveFx()) {
38612             return;
38613         }
38614         if(this.isSlid){
38615             this.afterSlideIn();
38616             skipAnim = true;
38617         }
38618         this.collapsed = false;
38619         if(this.config.animate && skipAnim !== true){
38620             this.animateExpand();
38621         }else{
38622             this.el.show();
38623             if(this.split){
38624                 this.split.el.show();
38625             }
38626             this.collapsedEl.setLocation(-2000,-2000);
38627             this.collapsedEl.hide();
38628             this.fireEvent("invalidated", this);
38629             this.fireEvent("expanded", this);
38630         }
38631     },
38632 */
38633     animateExpand : function(){
38634         // overridden
38635     },
38636
38637     initTabs : function()
38638     {
38639         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38640         
38641         var ts = new Roo.bootstrap.panel.Tabs({
38642             el: this.bodyEl.dom,
38643             region : this,
38644             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38645             disableTooltips: this.config.disableTabTips,
38646             toolbar : this.config.toolbar
38647         });
38648         
38649         if(this.config.hideTabs){
38650             ts.stripWrap.setDisplayed(false);
38651         }
38652         this.tabs = ts;
38653         ts.resizeTabs = this.config.resizeTabs === true;
38654         ts.minTabWidth = this.config.minTabWidth || 40;
38655         ts.maxTabWidth = this.config.maxTabWidth || 250;
38656         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38657         ts.monitorResize = false;
38658         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38659         ts.bodyEl.addClass('roo-layout-tabs-body');
38660         this.panels.each(this.initPanelAsTab, this);
38661     },
38662
38663     initPanelAsTab : function(panel){
38664         var ti = this.tabs.addTab(
38665             panel.getEl().id,
38666             panel.getTitle(),
38667             null,
38668             this.config.closeOnTab && panel.isClosable(),
38669             panel.tpl
38670         );
38671         if(panel.tabTip !== undefined){
38672             ti.setTooltip(panel.tabTip);
38673         }
38674         ti.on("activate", function(){
38675               this.setActivePanel(panel);
38676         }, this);
38677         
38678         if(this.config.closeOnTab){
38679             ti.on("beforeclose", function(t, e){
38680                 e.cancel = true;
38681                 this.remove(panel);
38682             }, this);
38683         }
38684         
38685         panel.tabItem = ti;
38686         
38687         return ti;
38688     },
38689
38690     updatePanelTitle : function(panel, title)
38691     {
38692         if(this.activePanel == panel){
38693             this.updateTitle(title);
38694         }
38695         if(this.tabs){
38696             var ti = this.tabs.getTab(panel.getEl().id);
38697             ti.setText(title);
38698             if(panel.tabTip !== undefined){
38699                 ti.setTooltip(panel.tabTip);
38700             }
38701         }
38702     },
38703
38704     updateTitle : function(title){
38705         if(this.titleTextEl && !this.config.title){
38706             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38707         }
38708     },
38709
38710     setActivePanel : function(panel)
38711     {
38712         panel = this.getPanel(panel);
38713         if(this.activePanel && this.activePanel != panel){
38714             if(this.activePanel.setActiveState(false) === false){
38715                 return;
38716             }
38717         }
38718         this.activePanel = panel;
38719         panel.setActiveState(true);
38720         if(this.panelSize){
38721             panel.setSize(this.panelSize.width, this.panelSize.height);
38722         }
38723         if(this.closeBtn){
38724             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38725         }
38726         this.updateTitle(panel.getTitle());
38727         if(this.tabs){
38728             this.fireEvent("invalidated", this);
38729         }
38730         this.fireEvent("panelactivated", this, panel);
38731     },
38732
38733     /**
38734      * Shows the specified panel.
38735      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38736      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38737      */
38738     showPanel : function(panel)
38739     {
38740         panel = this.getPanel(panel);
38741         if(panel){
38742             if(this.tabs){
38743                 var tab = this.tabs.getTab(panel.getEl().id);
38744                 if(tab.isHidden()){
38745                     this.tabs.unhideTab(tab.id);
38746                 }
38747                 tab.activate();
38748             }else{
38749                 this.setActivePanel(panel);
38750             }
38751         }
38752         return panel;
38753     },
38754
38755     /**
38756      * Get the active panel for this region.
38757      * @return {Roo.ContentPanel} The active panel or null
38758      */
38759     getActivePanel : function(){
38760         return this.activePanel;
38761     },
38762
38763     validateVisibility : function(){
38764         if(this.panels.getCount() < 1){
38765             this.updateTitle("&#160;");
38766             this.closeBtn.hide();
38767             this.hide();
38768         }else{
38769             if(!this.isVisible()){
38770                 this.show();
38771             }
38772         }
38773     },
38774
38775     /**
38776      * Adds the passed ContentPanel(s) to this region.
38777      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38778      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38779      */
38780     add : function(panel)
38781     {
38782         if(arguments.length > 1){
38783             for(var i = 0, len = arguments.length; i < len; i++) {
38784                 this.add(arguments[i]);
38785             }
38786             return null;
38787         }
38788         
38789         // if we have not been rendered yet, then we can not really do much of this..
38790         if (!this.bodyEl) {
38791             this.unrendered_panels.push(panel);
38792             return panel;
38793         }
38794         
38795         
38796         
38797         
38798         if(this.hasPanel(panel)){
38799             this.showPanel(panel);
38800             return panel;
38801         }
38802         panel.setRegion(this);
38803         this.panels.add(panel);
38804        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38805             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38806             // and hide them... ???
38807             this.bodyEl.dom.appendChild(panel.getEl().dom);
38808             if(panel.background !== true){
38809                 this.setActivePanel(panel);
38810             }
38811             this.fireEvent("paneladded", this, panel);
38812             return panel;
38813         }
38814         */
38815         if(!this.tabs){
38816             this.initTabs();
38817         }else{
38818             this.initPanelAsTab(panel);
38819         }
38820         
38821         
38822         if(panel.background !== true){
38823             this.tabs.activate(panel.getEl().id);
38824         }
38825         this.fireEvent("paneladded", this, panel);
38826         return panel;
38827     },
38828
38829     /**
38830      * Hides the tab for the specified panel.
38831      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38832      */
38833     hidePanel : function(panel){
38834         if(this.tabs && (panel = this.getPanel(panel))){
38835             this.tabs.hideTab(panel.getEl().id);
38836         }
38837     },
38838
38839     /**
38840      * Unhides the tab for a previously hidden panel.
38841      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38842      */
38843     unhidePanel : function(panel){
38844         if(this.tabs && (panel = this.getPanel(panel))){
38845             this.tabs.unhideTab(panel.getEl().id);
38846         }
38847     },
38848
38849     clearPanels : function(){
38850         while(this.panels.getCount() > 0){
38851              this.remove(this.panels.first());
38852         }
38853     },
38854
38855     /**
38856      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38857      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38858      * @param {Boolean} preservePanel Overrides the config preservePanel option
38859      * @return {Roo.ContentPanel} The panel that was removed
38860      */
38861     remove : function(panel, preservePanel)
38862     {
38863         panel = this.getPanel(panel);
38864         if(!panel){
38865             return null;
38866         }
38867         var e = {};
38868         this.fireEvent("beforeremove", this, panel, e);
38869         if(e.cancel === true){
38870             return null;
38871         }
38872         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38873         var panelId = panel.getId();
38874         this.panels.removeKey(panelId);
38875         if(preservePanel){
38876             document.body.appendChild(panel.getEl().dom);
38877         }
38878         if(this.tabs){
38879             this.tabs.removeTab(panel.getEl().id);
38880         }else if (!preservePanel){
38881             this.bodyEl.dom.removeChild(panel.getEl().dom);
38882         }
38883         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38884             var p = this.panels.first();
38885             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38886             tempEl.appendChild(p.getEl().dom);
38887             this.bodyEl.update("");
38888             this.bodyEl.dom.appendChild(p.getEl().dom);
38889             tempEl = null;
38890             this.updateTitle(p.getTitle());
38891             this.tabs = null;
38892             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38893             this.setActivePanel(p);
38894         }
38895         panel.setRegion(null);
38896         if(this.activePanel == panel){
38897             this.activePanel = null;
38898         }
38899         if(this.config.autoDestroy !== false && preservePanel !== true){
38900             try{panel.destroy();}catch(e){}
38901         }
38902         this.fireEvent("panelremoved", this, panel);
38903         return panel;
38904     },
38905
38906     /**
38907      * Returns the TabPanel component used by this region
38908      * @return {Roo.TabPanel}
38909      */
38910     getTabs : function(){
38911         return this.tabs;
38912     },
38913
38914     createTool : function(parentEl, className){
38915         var btn = Roo.DomHelper.append(parentEl, {
38916             tag: "div",
38917             cls: "x-layout-tools-button",
38918             children: [ {
38919                 tag: "div",
38920                 cls: "roo-layout-tools-button-inner " + className,
38921                 html: "&#160;"
38922             }]
38923         }, true);
38924         btn.addClassOnOver("roo-layout-tools-button-over");
38925         return btn;
38926     }
38927 });/*
38928  * Based on:
38929  * Ext JS Library 1.1.1
38930  * Copyright(c) 2006-2007, Ext JS, LLC.
38931  *
38932  * Originally Released Under LGPL - original licence link has changed is not relivant.
38933  *
38934  * Fork - LGPL
38935  * <script type="text/javascript">
38936  */
38937  
38938
38939
38940 /**
38941  * @class Roo.SplitLayoutRegion
38942  * @extends Roo.LayoutRegion
38943  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38944  */
38945 Roo.bootstrap.layout.Split = function(config){
38946     this.cursor = config.cursor;
38947     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38948 };
38949
38950 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38951 {
38952     splitTip : "Drag to resize.",
38953     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38954     useSplitTips : false,
38955
38956     applyConfig : function(config){
38957         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38958     },
38959     
38960     onRender : function(ctr,pos) {
38961         
38962         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38963         if(!this.config.split){
38964             return;
38965         }
38966         if(!this.split){
38967             
38968             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38969                             tag: "div",
38970                             id: this.el.id + "-split",
38971                             cls: "roo-layout-split roo-layout-split-"+this.position,
38972                             html: "&#160;"
38973             });
38974             /** The SplitBar for this region 
38975             * @type Roo.SplitBar */
38976             // does not exist yet...
38977             Roo.log([this.position, this.orientation]);
38978             
38979             this.split = new Roo.bootstrap.SplitBar({
38980                 dragElement : splitEl,
38981                 resizingElement: this.el,
38982                 orientation : this.orientation
38983             });
38984             
38985             this.split.on("moved", this.onSplitMove, this);
38986             this.split.useShim = this.config.useShim === true;
38987             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38988             if(this.useSplitTips){
38989                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38990             }
38991             //if(config.collapsible){
38992             //    this.split.el.on("dblclick", this.collapse,  this);
38993             //}
38994         }
38995         if(typeof this.config.minSize != "undefined"){
38996             this.split.minSize = this.config.minSize;
38997         }
38998         if(typeof this.config.maxSize != "undefined"){
38999             this.split.maxSize = this.config.maxSize;
39000         }
39001         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39002             this.hideSplitter();
39003         }
39004         
39005     },
39006
39007     getHMaxSize : function(){
39008          var cmax = this.config.maxSize || 10000;
39009          var center = this.mgr.getRegion("center");
39010          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39011     },
39012
39013     getVMaxSize : function(){
39014          var cmax = this.config.maxSize || 10000;
39015          var center = this.mgr.getRegion("center");
39016          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39017     },
39018
39019     onSplitMove : function(split, newSize){
39020         this.fireEvent("resized", this, newSize);
39021     },
39022     
39023     /** 
39024      * Returns the {@link Roo.SplitBar} for this region.
39025      * @return {Roo.SplitBar}
39026      */
39027     getSplitBar : function(){
39028         return this.split;
39029     },
39030     
39031     hide : function(){
39032         this.hideSplitter();
39033         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39034     },
39035
39036     hideSplitter : function(){
39037         if(this.split){
39038             this.split.el.setLocation(-2000,-2000);
39039             this.split.el.hide();
39040         }
39041     },
39042
39043     show : function(){
39044         if(this.split){
39045             this.split.el.show();
39046         }
39047         Roo.bootstrap.layout.Split.superclass.show.call(this);
39048     },
39049     
39050     beforeSlide: function(){
39051         if(Roo.isGecko){// firefox overflow auto bug workaround
39052             this.bodyEl.clip();
39053             if(this.tabs) {
39054                 this.tabs.bodyEl.clip();
39055             }
39056             if(this.activePanel){
39057                 this.activePanel.getEl().clip();
39058                 
39059                 if(this.activePanel.beforeSlide){
39060                     this.activePanel.beforeSlide();
39061                 }
39062             }
39063         }
39064     },
39065     
39066     afterSlide : function(){
39067         if(Roo.isGecko){// firefox overflow auto bug workaround
39068             this.bodyEl.unclip();
39069             if(this.tabs) {
39070                 this.tabs.bodyEl.unclip();
39071             }
39072             if(this.activePanel){
39073                 this.activePanel.getEl().unclip();
39074                 if(this.activePanel.afterSlide){
39075                     this.activePanel.afterSlide();
39076                 }
39077             }
39078         }
39079     },
39080
39081     initAutoHide : function(){
39082         if(this.autoHide !== false){
39083             if(!this.autoHideHd){
39084                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39085                 this.autoHideHd = {
39086                     "mouseout": function(e){
39087                         if(!e.within(this.el, true)){
39088                             st.delay(500);
39089                         }
39090                     },
39091                     "mouseover" : function(e){
39092                         st.cancel();
39093                     },
39094                     scope : this
39095                 };
39096             }
39097             this.el.on(this.autoHideHd);
39098         }
39099     },
39100
39101     clearAutoHide : function(){
39102         if(this.autoHide !== false){
39103             this.el.un("mouseout", this.autoHideHd.mouseout);
39104             this.el.un("mouseover", this.autoHideHd.mouseover);
39105         }
39106     },
39107
39108     clearMonitor : function(){
39109         Roo.get(document).un("click", this.slideInIf, this);
39110     },
39111
39112     // these names are backwards but not changed for compat
39113     slideOut : function(){
39114         if(this.isSlid || this.el.hasActiveFx()){
39115             return;
39116         }
39117         this.isSlid = true;
39118         if(this.collapseBtn){
39119             this.collapseBtn.hide();
39120         }
39121         this.closeBtnState = this.closeBtn.getStyle('display');
39122         this.closeBtn.hide();
39123         if(this.stickBtn){
39124             this.stickBtn.show();
39125         }
39126         this.el.show();
39127         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39128         this.beforeSlide();
39129         this.el.setStyle("z-index", 10001);
39130         this.el.slideIn(this.getSlideAnchor(), {
39131             callback: function(){
39132                 this.afterSlide();
39133                 this.initAutoHide();
39134                 Roo.get(document).on("click", this.slideInIf, this);
39135                 this.fireEvent("slideshow", this);
39136             },
39137             scope: this,
39138             block: true
39139         });
39140     },
39141
39142     afterSlideIn : function(){
39143         this.clearAutoHide();
39144         this.isSlid = false;
39145         this.clearMonitor();
39146         this.el.setStyle("z-index", "");
39147         if(this.collapseBtn){
39148             this.collapseBtn.show();
39149         }
39150         this.closeBtn.setStyle('display', this.closeBtnState);
39151         if(this.stickBtn){
39152             this.stickBtn.hide();
39153         }
39154         this.fireEvent("slidehide", this);
39155     },
39156
39157     slideIn : function(cb){
39158         if(!this.isSlid || this.el.hasActiveFx()){
39159             Roo.callback(cb);
39160             return;
39161         }
39162         this.isSlid = false;
39163         this.beforeSlide();
39164         this.el.slideOut(this.getSlideAnchor(), {
39165             callback: function(){
39166                 this.el.setLeftTop(-10000, -10000);
39167                 this.afterSlide();
39168                 this.afterSlideIn();
39169                 Roo.callback(cb);
39170             },
39171             scope: this,
39172             block: true
39173         });
39174     },
39175     
39176     slideInIf : function(e){
39177         if(!e.within(this.el)){
39178             this.slideIn();
39179         }
39180     },
39181
39182     animateCollapse : function(){
39183         this.beforeSlide();
39184         this.el.setStyle("z-index", 20000);
39185         var anchor = this.getSlideAnchor();
39186         this.el.slideOut(anchor, {
39187             callback : function(){
39188                 this.el.setStyle("z-index", "");
39189                 this.collapsedEl.slideIn(anchor, {duration:.3});
39190                 this.afterSlide();
39191                 this.el.setLocation(-10000,-10000);
39192                 this.el.hide();
39193                 this.fireEvent("collapsed", this);
39194             },
39195             scope: this,
39196             block: true
39197         });
39198     },
39199
39200     animateExpand : function(){
39201         this.beforeSlide();
39202         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39203         this.el.setStyle("z-index", 20000);
39204         this.collapsedEl.hide({
39205             duration:.1
39206         });
39207         this.el.slideIn(this.getSlideAnchor(), {
39208             callback : function(){
39209                 this.el.setStyle("z-index", "");
39210                 this.afterSlide();
39211                 if(this.split){
39212                     this.split.el.show();
39213                 }
39214                 this.fireEvent("invalidated", this);
39215                 this.fireEvent("expanded", this);
39216             },
39217             scope: this,
39218             block: true
39219         });
39220     },
39221
39222     anchors : {
39223         "west" : "left",
39224         "east" : "right",
39225         "north" : "top",
39226         "south" : "bottom"
39227     },
39228
39229     sanchors : {
39230         "west" : "l",
39231         "east" : "r",
39232         "north" : "t",
39233         "south" : "b"
39234     },
39235
39236     canchors : {
39237         "west" : "tl-tr",
39238         "east" : "tr-tl",
39239         "north" : "tl-bl",
39240         "south" : "bl-tl"
39241     },
39242
39243     getAnchor : function(){
39244         return this.anchors[this.position];
39245     },
39246
39247     getCollapseAnchor : function(){
39248         return this.canchors[this.position];
39249     },
39250
39251     getSlideAnchor : function(){
39252         return this.sanchors[this.position];
39253     },
39254
39255     getAlignAdj : function(){
39256         var cm = this.cmargins;
39257         switch(this.position){
39258             case "west":
39259                 return [0, 0];
39260             break;
39261             case "east":
39262                 return [0, 0];
39263             break;
39264             case "north":
39265                 return [0, 0];
39266             break;
39267             case "south":
39268                 return [0, 0];
39269             break;
39270         }
39271     },
39272
39273     getExpandAdj : function(){
39274         var c = this.collapsedEl, cm = this.cmargins;
39275         switch(this.position){
39276             case "west":
39277                 return [-(cm.right+c.getWidth()+cm.left), 0];
39278             break;
39279             case "east":
39280                 return [cm.right+c.getWidth()+cm.left, 0];
39281             break;
39282             case "north":
39283                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39284             break;
39285             case "south":
39286                 return [0, cm.top+cm.bottom+c.getHeight()];
39287             break;
39288         }
39289     }
39290 });/*
39291  * Based on:
39292  * Ext JS Library 1.1.1
39293  * Copyright(c) 2006-2007, Ext JS, LLC.
39294  *
39295  * Originally Released Under LGPL - original licence link has changed is not relivant.
39296  *
39297  * Fork - LGPL
39298  * <script type="text/javascript">
39299  */
39300 /*
39301  * These classes are private internal classes
39302  */
39303 Roo.bootstrap.layout.Center = function(config){
39304     config.region = "center";
39305     Roo.bootstrap.layout.Region.call(this, config);
39306     this.visible = true;
39307     this.minWidth = config.minWidth || 20;
39308     this.minHeight = config.minHeight || 20;
39309 };
39310
39311 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39312     hide : function(){
39313         // center panel can't be hidden
39314     },
39315     
39316     show : function(){
39317         // center panel can't be hidden
39318     },
39319     
39320     getMinWidth: function(){
39321         return this.minWidth;
39322     },
39323     
39324     getMinHeight: function(){
39325         return this.minHeight;
39326     }
39327 });
39328
39329
39330
39331
39332  
39333
39334
39335
39336
39337
39338
39339 Roo.bootstrap.layout.North = function(config)
39340 {
39341     config.region = 'north';
39342     config.cursor = 'n-resize';
39343     
39344     Roo.bootstrap.layout.Split.call(this, config);
39345     
39346     
39347     if(this.split){
39348         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39349         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39350         this.split.el.addClass("roo-layout-split-v");
39351     }
39352     //var size = config.initialSize || config.height;
39353     //if(this.el && typeof size != "undefined"){
39354     //    this.el.setHeight(size);
39355     //}
39356 };
39357 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39358 {
39359     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39360      
39361      
39362     onRender : function(ctr, pos)
39363     {
39364         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39365         var size = this.config.initialSize || this.config.height;
39366         if(this.el && typeof size != "undefined"){
39367             this.el.setHeight(size);
39368         }
39369     
39370     },
39371     
39372     getBox : function(){
39373         if(this.collapsed){
39374             return this.collapsedEl.getBox();
39375         }
39376         var box = this.el.getBox();
39377         if(this.split){
39378             box.height += this.split.el.getHeight();
39379         }
39380         return box;
39381     },
39382     
39383     updateBox : function(box){
39384         if(this.split && !this.collapsed){
39385             box.height -= this.split.el.getHeight();
39386             this.split.el.setLeft(box.x);
39387             this.split.el.setTop(box.y+box.height);
39388             this.split.el.setWidth(box.width);
39389         }
39390         if(this.collapsed){
39391             this.updateBody(box.width, null);
39392         }
39393         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39394     }
39395 });
39396
39397
39398
39399
39400
39401 Roo.bootstrap.layout.South = function(config){
39402     config.region = 'south';
39403     config.cursor = 's-resize';
39404     Roo.bootstrap.layout.Split.call(this, config);
39405     if(this.split){
39406         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39407         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39408         this.split.el.addClass("roo-layout-split-v");
39409     }
39410     
39411 };
39412
39413 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39414     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39415     
39416     onRender : function(ctr, pos)
39417     {
39418         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39419         var size = this.config.initialSize || this.config.height;
39420         if(this.el && typeof size != "undefined"){
39421             this.el.setHeight(size);
39422         }
39423     
39424     },
39425     
39426     getBox : function(){
39427         if(this.collapsed){
39428             return this.collapsedEl.getBox();
39429         }
39430         var box = this.el.getBox();
39431         if(this.split){
39432             var sh = this.split.el.getHeight();
39433             box.height += sh;
39434             box.y -= sh;
39435         }
39436         return box;
39437     },
39438     
39439     updateBox : function(box){
39440         if(this.split && !this.collapsed){
39441             var sh = this.split.el.getHeight();
39442             box.height -= sh;
39443             box.y += sh;
39444             this.split.el.setLeft(box.x);
39445             this.split.el.setTop(box.y-sh);
39446             this.split.el.setWidth(box.width);
39447         }
39448         if(this.collapsed){
39449             this.updateBody(box.width, null);
39450         }
39451         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39452     }
39453 });
39454
39455 Roo.bootstrap.layout.East = function(config){
39456     config.region = "east";
39457     config.cursor = "e-resize";
39458     Roo.bootstrap.layout.Split.call(this, config);
39459     if(this.split){
39460         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39461         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39462         this.split.el.addClass("roo-layout-split-h");
39463     }
39464     
39465 };
39466 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39467     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39468     
39469     onRender : function(ctr, pos)
39470     {
39471         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39472         var size = this.config.initialSize || this.config.width;
39473         if(this.el && typeof size != "undefined"){
39474             this.el.setWidth(size);
39475         }
39476     
39477     },
39478     
39479     getBox : function(){
39480         if(this.collapsed){
39481             return this.collapsedEl.getBox();
39482         }
39483         var box = this.el.getBox();
39484         if(this.split){
39485             var sw = this.split.el.getWidth();
39486             box.width += sw;
39487             box.x -= sw;
39488         }
39489         return box;
39490     },
39491
39492     updateBox : function(box){
39493         if(this.split && !this.collapsed){
39494             var sw = this.split.el.getWidth();
39495             box.width -= sw;
39496             this.split.el.setLeft(box.x);
39497             this.split.el.setTop(box.y);
39498             this.split.el.setHeight(box.height);
39499             box.x += sw;
39500         }
39501         if(this.collapsed){
39502             this.updateBody(null, box.height);
39503         }
39504         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39505     }
39506 });
39507
39508 Roo.bootstrap.layout.West = function(config){
39509     config.region = "west";
39510     config.cursor = "w-resize";
39511     
39512     Roo.bootstrap.layout.Split.call(this, config);
39513     if(this.split){
39514         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39515         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39516         this.split.el.addClass("roo-layout-split-h");
39517     }
39518     
39519 };
39520 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39521     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39522     
39523     onRender: function(ctr, pos)
39524     {
39525         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39526         var size = this.config.initialSize || this.config.width;
39527         if(typeof size != "undefined"){
39528             this.el.setWidth(size);
39529         }
39530     },
39531     
39532     getBox : function(){
39533         if(this.collapsed){
39534             return this.collapsedEl.getBox();
39535         }
39536         var box = this.el.getBox();
39537         if (box.width == 0) {
39538             box.width = this.config.width; // kludge?
39539         }
39540         if(this.split){
39541             box.width += this.split.el.getWidth();
39542         }
39543         return box;
39544     },
39545     
39546     updateBox : function(box){
39547         if(this.split && !this.collapsed){
39548             var sw = this.split.el.getWidth();
39549             box.width -= sw;
39550             this.split.el.setLeft(box.x+box.width);
39551             this.split.el.setTop(box.y);
39552             this.split.el.setHeight(box.height);
39553         }
39554         if(this.collapsed){
39555             this.updateBody(null, box.height);
39556         }
39557         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39558     }
39559 });Roo.namespace("Roo.bootstrap.panel");/*
39560  * Based on:
39561  * Ext JS Library 1.1.1
39562  * Copyright(c) 2006-2007, Ext JS, LLC.
39563  *
39564  * Originally Released Under LGPL - original licence link has changed is not relivant.
39565  *
39566  * Fork - LGPL
39567  * <script type="text/javascript">
39568  */
39569 /**
39570  * @class Roo.ContentPanel
39571  * @extends Roo.util.Observable
39572  * A basic ContentPanel element.
39573  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39574  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39575  * @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
39576  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39577  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39578  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39579  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39580  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39581  * @cfg {String} title          The title for this panel
39582  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39583  * @cfg {String} url            Calls {@link #setUrl} with this value
39584  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39585  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39586  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39587  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39588  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39589  * @cfg {Boolean} badges render the badges
39590  * @cfg {String} cls  extra classes to use  
39591  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39592
39593  * @constructor
39594  * Create a new ContentPanel.
39595  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39596  * @param {String/Object} config A string to set only the title or a config object
39597  * @param {String} content (optional) Set the HTML content for this panel
39598  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39599  */
39600 Roo.bootstrap.panel.Content = function( config){
39601     
39602     this.tpl = config.tpl || false;
39603     
39604     var el = config.el;
39605     var content = config.content;
39606
39607     if(config.autoCreate){ // xtype is available if this is called from factory
39608         el = Roo.id();
39609     }
39610     this.el = Roo.get(el);
39611     if(!this.el && config && config.autoCreate){
39612         if(typeof config.autoCreate == "object"){
39613             if(!config.autoCreate.id){
39614                 config.autoCreate.id = config.id||el;
39615             }
39616             this.el = Roo.DomHelper.append(document.body,
39617                         config.autoCreate, true);
39618         }else{
39619             var elcfg =  {
39620                 tag: "div",
39621                 cls: (config.cls || '') +
39622                     (config.background ? ' bg-' + config.background : '') +
39623                     " roo-layout-inactive-content",
39624                 id: config.id||el
39625             };
39626             if (config.iframe) {
39627                 elcfg.cn = [
39628                     {
39629                         tag : 'iframe',
39630                         style : 'border: 0px',
39631                         src : 'about:blank'
39632                     }
39633                 ];
39634             }
39635               
39636             if (config.html) {
39637                 elcfg.html = config.html;
39638                 
39639             }
39640                         
39641             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39642             if (config.iframe) {
39643                 this.iframeEl = this.el.select('iframe',true).first();
39644             }
39645             
39646         }
39647     } 
39648     this.closable = false;
39649     this.loaded = false;
39650     this.active = false;
39651    
39652       
39653     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39654         
39655         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39656         
39657         this.wrapEl = this.el; //this.el.wrap();
39658         var ti = [];
39659         if (config.toolbar.items) {
39660             ti = config.toolbar.items ;
39661             delete config.toolbar.items ;
39662         }
39663         
39664         var nitems = [];
39665         this.toolbar.render(this.wrapEl, 'before');
39666         for(var i =0;i < ti.length;i++) {
39667           //  Roo.log(['add child', items[i]]);
39668             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39669         }
39670         this.toolbar.items = nitems;
39671         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39672         delete config.toolbar;
39673         
39674     }
39675     /*
39676     // xtype created footer. - not sure if will work as we normally have to render first..
39677     if (this.footer && !this.footer.el && this.footer.xtype) {
39678         if (!this.wrapEl) {
39679             this.wrapEl = this.el.wrap();
39680         }
39681     
39682         this.footer.container = this.wrapEl.createChild();
39683          
39684         this.footer = Roo.factory(this.footer, Roo);
39685         
39686     }
39687     */
39688     
39689      if(typeof config == "string"){
39690         this.title = config;
39691     }else{
39692         Roo.apply(this, config);
39693     }
39694     
39695     if(this.resizeEl){
39696         this.resizeEl = Roo.get(this.resizeEl, true);
39697     }else{
39698         this.resizeEl = this.el;
39699     }
39700     // handle view.xtype
39701     
39702  
39703     
39704     
39705     this.addEvents({
39706         /**
39707          * @event activate
39708          * Fires when this panel is activated. 
39709          * @param {Roo.ContentPanel} this
39710          */
39711         "activate" : true,
39712         /**
39713          * @event deactivate
39714          * Fires when this panel is activated. 
39715          * @param {Roo.ContentPanel} this
39716          */
39717         "deactivate" : true,
39718
39719         /**
39720          * @event resize
39721          * Fires when this panel is resized if fitToFrame is true.
39722          * @param {Roo.ContentPanel} this
39723          * @param {Number} width The width after any component adjustments
39724          * @param {Number} height The height after any component adjustments
39725          */
39726         "resize" : true,
39727         
39728          /**
39729          * @event render
39730          * Fires when this tab is created
39731          * @param {Roo.ContentPanel} this
39732          */
39733         "render" : true
39734         
39735         
39736         
39737     });
39738     
39739
39740     
39741     
39742     if(this.autoScroll && !this.iframe){
39743         this.resizeEl.setStyle("overflow", "auto");
39744     } else {
39745         // fix randome scrolling
39746         //this.el.on('scroll', function() {
39747         //    Roo.log('fix random scolling');
39748         //    this.scrollTo('top',0); 
39749         //});
39750     }
39751     content = content || this.content;
39752     if(content){
39753         this.setContent(content);
39754     }
39755     if(config && config.url){
39756         this.setUrl(this.url, this.params, this.loadOnce);
39757     }
39758     
39759     
39760     
39761     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39762     
39763     if (this.view && typeof(this.view.xtype) != 'undefined') {
39764         this.view.el = this.el.appendChild(document.createElement("div"));
39765         this.view = Roo.factory(this.view); 
39766         this.view.render  &&  this.view.render(false, '');  
39767     }
39768     
39769     
39770     this.fireEvent('render', this);
39771 };
39772
39773 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39774     
39775     cls : '',
39776     background : '',
39777     
39778     tabTip : '',
39779     
39780     iframe : false,
39781     iframeEl : false,
39782     
39783     setRegion : function(region){
39784         this.region = region;
39785         this.setActiveClass(region && !this.background);
39786     },
39787     
39788     
39789     setActiveClass: function(state)
39790     {
39791         if(state){
39792            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39793            this.el.setStyle('position','relative');
39794         }else{
39795            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39796            this.el.setStyle('position', 'absolute');
39797         } 
39798     },
39799     
39800     /**
39801      * Returns the toolbar for this Panel if one was configured. 
39802      * @return {Roo.Toolbar} 
39803      */
39804     getToolbar : function(){
39805         return this.toolbar;
39806     },
39807     
39808     setActiveState : function(active)
39809     {
39810         this.active = active;
39811         this.setActiveClass(active);
39812         if(!active){
39813             if(this.fireEvent("deactivate", this) === false){
39814                 return false;
39815             }
39816             return true;
39817         }
39818         this.fireEvent("activate", this);
39819         return true;
39820     },
39821     /**
39822      * Updates this panel's element (not for iframe)
39823      * @param {String} content The new content
39824      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39825     */
39826     setContent : function(content, loadScripts){
39827         if (this.iframe) {
39828             return;
39829         }
39830         
39831         this.el.update(content, loadScripts);
39832     },
39833
39834     ignoreResize : function(w, h){
39835         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39836             return true;
39837         }else{
39838             this.lastSize = {width: w, height: h};
39839             return false;
39840         }
39841     },
39842     /**
39843      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39844      * @return {Roo.UpdateManager} The UpdateManager
39845      */
39846     getUpdateManager : function(){
39847         if (this.iframe) {
39848             return false;
39849         }
39850         return this.el.getUpdateManager();
39851     },
39852      /**
39853      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39854      * Does not work with IFRAME contents
39855      * @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:
39856 <pre><code>
39857 panel.load({
39858     url: "your-url.php",
39859     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39860     callback: yourFunction,
39861     scope: yourObject, //(optional scope)
39862     discardUrl: false,
39863     nocache: false,
39864     text: "Loading...",
39865     timeout: 30,
39866     scripts: false
39867 });
39868 </code></pre>
39869      
39870      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39871      * 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.
39872      * @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}
39873      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39874      * @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.
39875      * @return {Roo.ContentPanel} this
39876      */
39877     load : function(){
39878         
39879         if (this.iframe) {
39880             return this;
39881         }
39882         
39883         var um = this.el.getUpdateManager();
39884         um.update.apply(um, arguments);
39885         return this;
39886     },
39887
39888
39889     /**
39890      * 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.
39891      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39892      * @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)
39893      * @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)
39894      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39895      */
39896     setUrl : function(url, params, loadOnce){
39897         if (this.iframe) {
39898             this.iframeEl.dom.src = url;
39899             return false;
39900         }
39901         
39902         if(this.refreshDelegate){
39903             this.removeListener("activate", this.refreshDelegate);
39904         }
39905         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39906         this.on("activate", this.refreshDelegate);
39907         return this.el.getUpdateManager();
39908     },
39909     
39910     _handleRefresh : function(url, params, loadOnce){
39911         if(!loadOnce || !this.loaded){
39912             var updater = this.el.getUpdateManager();
39913             updater.update(url, params, this._setLoaded.createDelegate(this));
39914         }
39915     },
39916     
39917     _setLoaded : function(){
39918         this.loaded = true;
39919     }, 
39920     
39921     /**
39922      * Returns this panel's id
39923      * @return {String} 
39924      */
39925     getId : function(){
39926         return this.el.id;
39927     },
39928     
39929     /** 
39930      * Returns this panel's element - used by regiosn to add.
39931      * @return {Roo.Element} 
39932      */
39933     getEl : function(){
39934         return this.wrapEl || this.el;
39935     },
39936     
39937    
39938     
39939     adjustForComponents : function(width, height)
39940     {
39941         //Roo.log('adjustForComponents ');
39942         if(this.resizeEl != this.el){
39943             width -= this.el.getFrameWidth('lr');
39944             height -= this.el.getFrameWidth('tb');
39945         }
39946         if(this.toolbar){
39947             var te = this.toolbar.getEl();
39948             te.setWidth(width);
39949             height -= te.getHeight();
39950         }
39951         if(this.footer){
39952             var te = this.footer.getEl();
39953             te.setWidth(width);
39954             height -= te.getHeight();
39955         }
39956         
39957         
39958         if(this.adjustments){
39959             width += this.adjustments[0];
39960             height += this.adjustments[1];
39961         }
39962         return {"width": width, "height": height};
39963     },
39964     
39965     setSize : function(width, height){
39966         if(this.fitToFrame && !this.ignoreResize(width, height)){
39967             if(this.fitContainer && this.resizeEl != this.el){
39968                 this.el.setSize(width, height);
39969             }
39970             var size = this.adjustForComponents(width, height);
39971             if (this.iframe) {
39972                 this.iframeEl.setSize(width,height);
39973             }
39974             
39975             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39976             this.fireEvent('resize', this, size.width, size.height);
39977             
39978             
39979         }
39980     },
39981     
39982     /**
39983      * Returns this panel's title
39984      * @return {String} 
39985      */
39986     getTitle : function(){
39987         
39988         if (typeof(this.title) != 'object') {
39989             return this.title;
39990         }
39991         
39992         var t = '';
39993         for (var k in this.title) {
39994             if (!this.title.hasOwnProperty(k)) {
39995                 continue;
39996             }
39997             
39998             if (k.indexOf('-') >= 0) {
39999                 var s = k.split('-');
40000                 for (var i = 0; i<s.length; i++) {
40001                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40002                 }
40003             } else {
40004                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40005             }
40006         }
40007         return t;
40008     },
40009     
40010     /**
40011      * Set this panel's title
40012      * @param {String} title
40013      */
40014     setTitle : function(title){
40015         this.title = title;
40016         if(this.region){
40017             this.region.updatePanelTitle(this, title);
40018         }
40019     },
40020     
40021     /**
40022      * Returns true is this panel was configured to be closable
40023      * @return {Boolean} 
40024      */
40025     isClosable : function(){
40026         return this.closable;
40027     },
40028     
40029     beforeSlide : function(){
40030         this.el.clip();
40031         this.resizeEl.clip();
40032     },
40033     
40034     afterSlide : function(){
40035         this.el.unclip();
40036         this.resizeEl.unclip();
40037     },
40038     
40039     /**
40040      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40041      *   Will fail silently if the {@link #setUrl} method has not been called.
40042      *   This does not activate the panel, just updates its content.
40043      */
40044     refresh : function(){
40045         if(this.refreshDelegate){
40046            this.loaded = false;
40047            this.refreshDelegate();
40048         }
40049     },
40050     
40051     /**
40052      * Destroys this panel
40053      */
40054     destroy : function(){
40055         this.el.removeAllListeners();
40056         var tempEl = document.createElement("span");
40057         tempEl.appendChild(this.el.dom);
40058         tempEl.innerHTML = "";
40059         this.el.remove();
40060         this.el = null;
40061     },
40062     
40063     /**
40064      * form - if the content panel contains a form - this is a reference to it.
40065      * @type {Roo.form.Form}
40066      */
40067     form : false,
40068     /**
40069      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40070      *    This contains a reference to it.
40071      * @type {Roo.View}
40072      */
40073     view : false,
40074     
40075       /**
40076      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40077      * <pre><code>
40078
40079 layout.addxtype({
40080        xtype : 'Form',
40081        items: [ .... ]
40082    }
40083 );
40084
40085 </code></pre>
40086      * @param {Object} cfg Xtype definition of item to add.
40087      */
40088     
40089     
40090     getChildContainer: function () {
40091         return this.getEl();
40092     }
40093     
40094     
40095     /*
40096         var  ret = new Roo.factory(cfg);
40097         return ret;
40098         
40099         
40100         // add form..
40101         if (cfg.xtype.match(/^Form$/)) {
40102             
40103             var el;
40104             //if (this.footer) {
40105             //    el = this.footer.container.insertSibling(false, 'before');
40106             //} else {
40107                 el = this.el.createChild();
40108             //}
40109
40110             this.form = new  Roo.form.Form(cfg);
40111             
40112             
40113             if ( this.form.allItems.length) {
40114                 this.form.render(el.dom);
40115             }
40116             return this.form;
40117         }
40118         // should only have one of theses..
40119         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40120             // views.. should not be just added - used named prop 'view''
40121             
40122             cfg.el = this.el.appendChild(document.createElement("div"));
40123             // factory?
40124             
40125             var ret = new Roo.factory(cfg);
40126              
40127              ret.render && ret.render(false, ''); // render blank..
40128             this.view = ret;
40129             return ret;
40130         }
40131         return false;
40132     }
40133     \*/
40134 });
40135  
40136 /**
40137  * @class Roo.bootstrap.panel.Grid
40138  * @extends Roo.bootstrap.panel.Content
40139  * @constructor
40140  * Create a new GridPanel.
40141  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40142  * @param {Object} config A the config object
40143   
40144  */
40145
40146
40147
40148 Roo.bootstrap.panel.Grid = function(config)
40149 {
40150     
40151       
40152     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40153         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40154
40155     config.el = this.wrapper;
40156     //this.el = this.wrapper;
40157     
40158       if (config.container) {
40159         // ctor'ed from a Border/panel.grid
40160         
40161         
40162         this.wrapper.setStyle("overflow", "hidden");
40163         this.wrapper.addClass('roo-grid-container');
40164
40165     }
40166     
40167     
40168     if(config.toolbar){
40169         var tool_el = this.wrapper.createChild();    
40170         this.toolbar = Roo.factory(config.toolbar);
40171         var ti = [];
40172         if (config.toolbar.items) {
40173             ti = config.toolbar.items ;
40174             delete config.toolbar.items ;
40175         }
40176         
40177         var nitems = [];
40178         this.toolbar.render(tool_el);
40179         for(var i =0;i < ti.length;i++) {
40180           //  Roo.log(['add child', items[i]]);
40181             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40182         }
40183         this.toolbar.items = nitems;
40184         
40185         delete config.toolbar;
40186     }
40187     
40188     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40189     config.grid.scrollBody = true;;
40190     config.grid.monitorWindowResize = false; // turn off autosizing
40191     config.grid.autoHeight = false;
40192     config.grid.autoWidth = false;
40193     
40194     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40195     
40196     if (config.background) {
40197         // render grid on panel activation (if panel background)
40198         this.on('activate', function(gp) {
40199             if (!gp.grid.rendered) {
40200                 gp.grid.render(this.wrapper);
40201                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40202             }
40203         });
40204             
40205     } else {
40206         this.grid.render(this.wrapper);
40207         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40208
40209     }
40210     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40211     // ??? needed ??? config.el = this.wrapper;
40212     
40213     
40214     
40215   
40216     // xtype created footer. - not sure if will work as we normally have to render first..
40217     if (this.footer && !this.footer.el && this.footer.xtype) {
40218         
40219         var ctr = this.grid.getView().getFooterPanel(true);
40220         this.footer.dataSource = this.grid.dataSource;
40221         this.footer = Roo.factory(this.footer, Roo);
40222         this.footer.render(ctr);
40223         
40224     }
40225     
40226     
40227     
40228     
40229      
40230 };
40231
40232 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40233     getId : function(){
40234         return this.grid.id;
40235     },
40236     
40237     /**
40238      * Returns the grid for this panel
40239      * @return {Roo.bootstrap.Table} 
40240      */
40241     getGrid : function(){
40242         return this.grid;    
40243     },
40244     
40245     setSize : function(width, height){
40246         if(!this.ignoreResize(width, height)){
40247             var grid = this.grid;
40248             var size = this.adjustForComponents(width, height);
40249             // tfoot is not a footer?
40250           
40251             
40252             var gridel = grid.getGridEl();
40253             gridel.setSize(size.width, size.height);
40254             
40255             var tbd = grid.getGridEl().select('tbody', true).first();
40256             var thd = grid.getGridEl().select('thead',true).first();
40257             var tbf= grid.getGridEl().select('tfoot', true).first();
40258
40259             if (tbf) {
40260                 size.height -= tbf.getHeight();
40261             }
40262             if (thd) {
40263                 size.height -= thd.getHeight();
40264             }
40265             
40266             tbd.setSize(size.width, size.height );
40267             // this is for the account management tab -seems to work there.
40268             var thd = grid.getGridEl().select('thead',true).first();
40269             //if (tbd) {
40270             //    tbd.setSize(size.width, size.height - thd.getHeight());
40271             //}
40272              
40273             grid.autoSize();
40274         }
40275     },
40276      
40277     
40278     
40279     beforeSlide : function(){
40280         this.grid.getView().scroller.clip();
40281     },
40282     
40283     afterSlide : function(){
40284         this.grid.getView().scroller.unclip();
40285     },
40286     
40287     destroy : function(){
40288         this.grid.destroy();
40289         delete this.grid;
40290         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40291     }
40292 });
40293
40294 /**
40295  * @class Roo.bootstrap.panel.Nest
40296  * @extends Roo.bootstrap.panel.Content
40297  * @constructor
40298  * Create a new Panel, that can contain a layout.Border.
40299  * 
40300  * 
40301  * @param {Roo.BorderLayout} layout The layout for this panel
40302  * @param {String/Object} config A string to set only the title or a config object
40303  */
40304 Roo.bootstrap.panel.Nest = function(config)
40305 {
40306     // construct with only one argument..
40307     /* FIXME - implement nicer consturctors
40308     if (layout.layout) {
40309         config = layout;
40310         layout = config.layout;
40311         delete config.layout;
40312     }
40313     if (layout.xtype && !layout.getEl) {
40314         // then layout needs constructing..
40315         layout = Roo.factory(layout, Roo);
40316     }
40317     */
40318     
40319     config.el =  config.layout.getEl();
40320     
40321     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40322     
40323     config.layout.monitorWindowResize = false; // turn off autosizing
40324     this.layout = config.layout;
40325     this.layout.getEl().addClass("roo-layout-nested-layout");
40326     this.layout.parent = this;
40327     
40328     
40329     
40330     
40331 };
40332
40333 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40334
40335     setSize : function(width, height){
40336         if(!this.ignoreResize(width, height)){
40337             var size = this.adjustForComponents(width, height);
40338             var el = this.layout.getEl();
40339             if (size.height < 1) {
40340                 el.setWidth(size.width);   
40341             } else {
40342                 el.setSize(size.width, size.height);
40343             }
40344             var touch = el.dom.offsetWidth;
40345             this.layout.layout();
40346             // ie requires a double layout on the first pass
40347             if(Roo.isIE && !this.initialized){
40348                 this.initialized = true;
40349                 this.layout.layout();
40350             }
40351         }
40352     },
40353     
40354     // activate all subpanels if not currently active..
40355     
40356     setActiveState : function(active){
40357         this.active = active;
40358         this.setActiveClass(active);
40359         
40360         if(!active){
40361             this.fireEvent("deactivate", this);
40362             return;
40363         }
40364         
40365         this.fireEvent("activate", this);
40366         // not sure if this should happen before or after..
40367         if (!this.layout) {
40368             return; // should not happen..
40369         }
40370         var reg = false;
40371         for (var r in this.layout.regions) {
40372             reg = this.layout.getRegion(r);
40373             if (reg.getActivePanel()) {
40374                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40375                 reg.setActivePanel(reg.getActivePanel());
40376                 continue;
40377             }
40378             if (!reg.panels.length) {
40379                 continue;
40380             }
40381             reg.showPanel(reg.getPanel(0));
40382         }
40383         
40384         
40385         
40386         
40387     },
40388     
40389     /**
40390      * Returns the nested BorderLayout for this panel
40391      * @return {Roo.BorderLayout} 
40392      */
40393     getLayout : function(){
40394         return this.layout;
40395     },
40396     
40397      /**
40398      * Adds a xtype elements to the layout of the nested panel
40399      * <pre><code>
40400
40401 panel.addxtype({
40402        xtype : 'ContentPanel',
40403        region: 'west',
40404        items: [ .... ]
40405    }
40406 );
40407
40408 panel.addxtype({
40409         xtype : 'NestedLayoutPanel',
40410         region: 'west',
40411         layout: {
40412            center: { },
40413            west: { }   
40414         },
40415         items : [ ... list of content panels or nested layout panels.. ]
40416    }
40417 );
40418 </code></pre>
40419      * @param {Object} cfg Xtype definition of item to add.
40420      */
40421     addxtype : function(cfg) {
40422         return this.layout.addxtype(cfg);
40423     
40424     }
40425 });/*
40426  * Based on:
40427  * Ext JS Library 1.1.1
40428  * Copyright(c) 2006-2007, Ext JS, LLC.
40429  *
40430  * Originally Released Under LGPL - original licence link has changed is not relivant.
40431  *
40432  * Fork - LGPL
40433  * <script type="text/javascript">
40434  */
40435 /**
40436  * @class Roo.TabPanel
40437  * @extends Roo.util.Observable
40438  * A lightweight tab container.
40439  * <br><br>
40440  * Usage:
40441  * <pre><code>
40442 // basic tabs 1, built from existing content
40443 var tabs = new Roo.TabPanel("tabs1");
40444 tabs.addTab("script", "View Script");
40445 tabs.addTab("markup", "View Markup");
40446 tabs.activate("script");
40447
40448 // more advanced tabs, built from javascript
40449 var jtabs = new Roo.TabPanel("jtabs");
40450 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40451
40452 // set up the UpdateManager
40453 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40454 var updater = tab2.getUpdateManager();
40455 updater.setDefaultUrl("ajax1.htm");
40456 tab2.on('activate', updater.refresh, updater, true);
40457
40458 // Use setUrl for Ajax loading
40459 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40460 tab3.setUrl("ajax2.htm", null, true);
40461
40462 // Disabled tab
40463 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40464 tab4.disable();
40465
40466 jtabs.activate("jtabs-1");
40467  * </code></pre>
40468  * @constructor
40469  * Create a new TabPanel.
40470  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40471  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40472  */
40473 Roo.bootstrap.panel.Tabs = function(config){
40474     /**
40475     * The container element for this TabPanel.
40476     * @type Roo.Element
40477     */
40478     this.el = Roo.get(config.el);
40479     delete config.el;
40480     if(config){
40481         if(typeof config == "boolean"){
40482             this.tabPosition = config ? "bottom" : "top";
40483         }else{
40484             Roo.apply(this, config);
40485         }
40486     }
40487     
40488     if(this.tabPosition == "bottom"){
40489         // if tabs are at the bottom = create the body first.
40490         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40491         this.el.addClass("roo-tabs-bottom");
40492     }
40493     // next create the tabs holders
40494     
40495     if (this.tabPosition == "west"){
40496         
40497         var reg = this.region; // fake it..
40498         while (reg) {
40499             if (!reg.mgr.parent) {
40500                 break;
40501             }
40502             reg = reg.mgr.parent.region;
40503         }
40504         Roo.log("got nest?");
40505         Roo.log(reg);
40506         if (reg.mgr.getRegion('west')) {
40507             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40508             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40509             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40510             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40511             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40512         
40513             
40514         }
40515         
40516         
40517     } else {
40518      
40519         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40520         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40521         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40522         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40523     }
40524     
40525     
40526     if(Roo.isIE){
40527         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40528     }
40529     
40530     // finally - if tabs are at the top, then create the body last..
40531     if(this.tabPosition != "bottom"){
40532         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40533          * @type Roo.Element
40534          */
40535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40536         this.el.addClass("roo-tabs-top");
40537     }
40538     this.items = [];
40539
40540     this.bodyEl.setStyle("position", "relative");
40541
40542     this.active = null;
40543     this.activateDelegate = this.activate.createDelegate(this);
40544
40545     this.addEvents({
40546         /**
40547          * @event tabchange
40548          * Fires when the active tab changes
40549          * @param {Roo.TabPanel} this
40550          * @param {Roo.TabPanelItem} activePanel The new active tab
40551          */
40552         "tabchange": true,
40553         /**
40554          * @event beforetabchange
40555          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40556          * @param {Roo.TabPanel} this
40557          * @param {Object} e Set cancel to true on this object to cancel the tab change
40558          * @param {Roo.TabPanelItem} tab The tab being changed to
40559          */
40560         "beforetabchange" : true
40561     });
40562
40563     Roo.EventManager.onWindowResize(this.onResize, this);
40564     this.cpad = this.el.getPadding("lr");
40565     this.hiddenCount = 0;
40566
40567
40568     // toolbar on the tabbar support...
40569     if (this.toolbar) {
40570         alert("no toolbar support yet");
40571         this.toolbar  = false;
40572         /*
40573         var tcfg = this.toolbar;
40574         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40575         this.toolbar = new Roo.Toolbar(tcfg);
40576         if (Roo.isSafari) {
40577             var tbl = tcfg.container.child('table', true);
40578             tbl.setAttribute('width', '100%');
40579         }
40580         */
40581         
40582     }
40583    
40584
40585
40586     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40587 };
40588
40589 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40590     /*
40591      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40592      */
40593     tabPosition : "top",
40594     /*
40595      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40596      */
40597     currentTabWidth : 0,
40598     /*
40599      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40600      */
40601     minTabWidth : 40,
40602     /*
40603      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40604      */
40605     maxTabWidth : 250,
40606     /*
40607      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40608      */
40609     preferredTabWidth : 175,
40610     /*
40611      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40612      */
40613     resizeTabs : false,
40614     /*
40615      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40616      */
40617     monitorResize : true,
40618     /*
40619      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40620      */
40621     toolbar : false,  // set by caller..
40622     
40623     region : false, /// set by caller
40624     
40625     disableTooltips : true, // not used yet...
40626
40627     /**
40628      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40629      * @param {String} id The id of the div to use <b>or create</b>
40630      * @param {String} text The text for the tab
40631      * @param {String} content (optional) Content to put in the TabPanelItem body
40632      * @param {Boolean} closable (optional) True to create a close icon on the tab
40633      * @return {Roo.TabPanelItem} The created TabPanelItem
40634      */
40635     addTab : function(id, text, content, closable, tpl)
40636     {
40637         var item = new Roo.bootstrap.panel.TabItem({
40638             panel: this,
40639             id : id,
40640             text : text,
40641             closable : closable,
40642             tpl : tpl
40643         });
40644         this.addTabItem(item);
40645         if(content){
40646             item.setContent(content);
40647         }
40648         return item;
40649     },
40650
40651     /**
40652      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40653      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40654      * @return {Roo.TabPanelItem}
40655      */
40656     getTab : function(id){
40657         return this.items[id];
40658     },
40659
40660     /**
40661      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40662      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40663      */
40664     hideTab : function(id){
40665         var t = this.items[id];
40666         if(!t.isHidden()){
40667            t.setHidden(true);
40668            this.hiddenCount++;
40669            this.autoSizeTabs();
40670         }
40671     },
40672
40673     /**
40674      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40675      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40676      */
40677     unhideTab : function(id){
40678         var t = this.items[id];
40679         if(t.isHidden()){
40680            t.setHidden(false);
40681            this.hiddenCount--;
40682            this.autoSizeTabs();
40683         }
40684     },
40685
40686     /**
40687      * Adds an existing {@link Roo.TabPanelItem}.
40688      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40689      */
40690     addTabItem : function(item)
40691     {
40692         this.items[item.id] = item;
40693         this.items.push(item);
40694         this.autoSizeTabs();
40695       //  if(this.resizeTabs){
40696     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40697   //         this.autoSizeTabs();
40698 //        }else{
40699 //            item.autoSize();
40700        // }
40701     },
40702
40703     /**
40704      * Removes a {@link Roo.TabPanelItem}.
40705      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40706      */
40707     removeTab : function(id){
40708         var items = this.items;
40709         var tab = items[id];
40710         if(!tab) { return; }
40711         var index = items.indexOf(tab);
40712         if(this.active == tab && items.length > 1){
40713             var newTab = this.getNextAvailable(index);
40714             if(newTab) {
40715                 newTab.activate();
40716             }
40717         }
40718         this.stripEl.dom.removeChild(tab.pnode.dom);
40719         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40720             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40721         }
40722         items.splice(index, 1);
40723         delete this.items[tab.id];
40724         tab.fireEvent("close", tab);
40725         tab.purgeListeners();
40726         this.autoSizeTabs();
40727     },
40728
40729     getNextAvailable : function(start){
40730         var items = this.items;
40731         var index = start;
40732         // look for a next tab that will slide over to
40733         // replace the one being removed
40734         while(index < items.length){
40735             var item = items[++index];
40736             if(item && !item.isHidden()){
40737                 return item;
40738             }
40739         }
40740         // if one isn't found select the previous tab (on the left)
40741         index = start;
40742         while(index >= 0){
40743             var item = items[--index];
40744             if(item && !item.isHidden()){
40745                 return item;
40746             }
40747         }
40748         return null;
40749     },
40750
40751     /**
40752      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40753      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40754      */
40755     disableTab : function(id){
40756         var tab = this.items[id];
40757         if(tab && this.active != tab){
40758             tab.disable();
40759         }
40760     },
40761
40762     /**
40763      * Enables a {@link Roo.TabPanelItem} that is disabled.
40764      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40765      */
40766     enableTab : function(id){
40767         var tab = this.items[id];
40768         tab.enable();
40769     },
40770
40771     /**
40772      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40773      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40774      * @return {Roo.TabPanelItem} The TabPanelItem.
40775      */
40776     activate : function(id)
40777     {
40778         //Roo.log('activite:'  + id);
40779         
40780         var tab = this.items[id];
40781         if(!tab){
40782             return null;
40783         }
40784         if(tab == this.active || tab.disabled){
40785             return tab;
40786         }
40787         var e = {};
40788         this.fireEvent("beforetabchange", this, e, tab);
40789         if(e.cancel !== true && !tab.disabled){
40790             if(this.active){
40791                 this.active.hide();
40792             }
40793             this.active = this.items[id];
40794             this.active.show();
40795             this.fireEvent("tabchange", this, this.active);
40796         }
40797         return tab;
40798     },
40799
40800     /**
40801      * Gets the active {@link Roo.TabPanelItem}.
40802      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40803      */
40804     getActiveTab : function(){
40805         return this.active;
40806     },
40807
40808     /**
40809      * Updates the tab body element to fit the height of the container element
40810      * for overflow scrolling
40811      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40812      */
40813     syncHeight : function(targetHeight){
40814         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40815         var bm = this.bodyEl.getMargins();
40816         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40817         this.bodyEl.setHeight(newHeight);
40818         return newHeight;
40819     },
40820
40821     onResize : function(){
40822         if(this.monitorResize){
40823             this.autoSizeTabs();
40824         }
40825     },
40826
40827     /**
40828      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40829      */
40830     beginUpdate : function(){
40831         this.updating = true;
40832     },
40833
40834     /**
40835      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40836      */
40837     endUpdate : function(){
40838         this.updating = false;
40839         this.autoSizeTabs();
40840     },
40841
40842     /**
40843      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40844      */
40845     autoSizeTabs : function()
40846     {
40847         var count = this.items.length;
40848         var vcount = count - this.hiddenCount;
40849         
40850         if (vcount < 2) {
40851             this.stripEl.hide();
40852         } else {
40853             this.stripEl.show();
40854         }
40855         
40856         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40857             return;
40858         }
40859         
40860         
40861         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40862         var availWidth = Math.floor(w / vcount);
40863         var b = this.stripBody;
40864         if(b.getWidth() > w){
40865             var tabs = this.items;
40866             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40867             if(availWidth < this.minTabWidth){
40868                 /*if(!this.sleft){    // incomplete scrolling code
40869                     this.createScrollButtons();
40870                 }
40871                 this.showScroll();
40872                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40873             }
40874         }else{
40875             if(this.currentTabWidth < this.preferredTabWidth){
40876                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40877             }
40878         }
40879     },
40880
40881     /**
40882      * Returns the number of tabs in this TabPanel.
40883      * @return {Number}
40884      */
40885      getCount : function(){
40886          return this.items.length;
40887      },
40888
40889     /**
40890      * Resizes all the tabs to the passed width
40891      * @param {Number} The new width
40892      */
40893     setTabWidth : function(width){
40894         this.currentTabWidth = width;
40895         for(var i = 0, len = this.items.length; i < len; i++) {
40896                 if(!this.items[i].isHidden()) {
40897                 this.items[i].setWidth(width);
40898             }
40899         }
40900     },
40901
40902     /**
40903      * Destroys this TabPanel
40904      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40905      */
40906     destroy : function(removeEl){
40907         Roo.EventManager.removeResizeListener(this.onResize, this);
40908         for(var i = 0, len = this.items.length; i < len; i++){
40909             this.items[i].purgeListeners();
40910         }
40911         if(removeEl === true){
40912             this.el.update("");
40913             this.el.remove();
40914         }
40915     },
40916     
40917     createStrip : function(container)
40918     {
40919         var strip = document.createElement("nav");
40920         strip.className = Roo.bootstrap.version == 4 ?
40921             "navbar-light bg-light" : 
40922             "navbar navbar-default"; //"x-tabs-wrap";
40923         container.appendChild(strip);
40924         return strip;
40925     },
40926     
40927     createStripList : function(strip)
40928     {
40929         // div wrapper for retard IE
40930         // returns the "tr" element.
40931         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40932         //'<div class="x-tabs-strip-wrap">'+
40933           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40934           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40935         return strip.firstChild; //.firstChild.firstChild.firstChild;
40936     },
40937     createBody : function(container)
40938     {
40939         var body = document.createElement("div");
40940         Roo.id(body, "tab-body");
40941         //Roo.fly(body).addClass("x-tabs-body");
40942         Roo.fly(body).addClass("tab-content");
40943         container.appendChild(body);
40944         return body;
40945     },
40946     createItemBody :function(bodyEl, id){
40947         var body = Roo.getDom(id);
40948         if(!body){
40949             body = document.createElement("div");
40950             body.id = id;
40951         }
40952         //Roo.fly(body).addClass("x-tabs-item-body");
40953         Roo.fly(body).addClass("tab-pane");
40954          bodyEl.insertBefore(body, bodyEl.firstChild);
40955         return body;
40956     },
40957     /** @private */
40958     createStripElements :  function(stripEl, text, closable, tpl)
40959     {
40960         var td = document.createElement("li"); // was td..
40961         td.className = 'nav-item';
40962         
40963         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40964         
40965         
40966         stripEl.appendChild(td);
40967         /*if(closable){
40968             td.className = "x-tabs-closable";
40969             if(!this.closeTpl){
40970                 this.closeTpl = new Roo.Template(
40971                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40972                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40973                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40974                 );
40975             }
40976             var el = this.closeTpl.overwrite(td, {"text": text});
40977             var close = el.getElementsByTagName("div")[0];
40978             var inner = el.getElementsByTagName("em")[0];
40979             return {"el": el, "close": close, "inner": inner};
40980         } else {
40981         */
40982         // not sure what this is..
40983 //            if(!this.tabTpl){
40984                 //this.tabTpl = new Roo.Template(
40985                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40986                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40987                 //);
40988 //                this.tabTpl = new Roo.Template(
40989 //                   '<a href="#">' +
40990 //                   '<span unselectable="on"' +
40991 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40992 //                            ' >{text}</span></a>'
40993 //                );
40994 //                
40995 //            }
40996
40997
40998             var template = tpl || this.tabTpl || false;
40999             
41000             if(!template){
41001                 template =  new Roo.Template(
41002                         Roo.bootstrap.version == 4 ? 
41003                             (
41004                                 '<a class="nav-link" href="#" unselectable="on"' +
41005                                      (this.disableTooltips ? '' : ' title="{text}"') +
41006                                      ' >{text}</a>'
41007                             ) : (
41008                                 '<a class="nav-link" href="#">' +
41009                                 '<span unselectable="on"' +
41010                                          (this.disableTooltips ? '' : ' title="{text}"') +
41011                                     ' >{text}</span></a>'
41012                             )
41013                 );
41014             }
41015             
41016             switch (typeof(template)) {
41017                 case 'object' :
41018                     break;
41019                 case 'string' :
41020                     template = new Roo.Template(template);
41021                     break;
41022                 default :
41023                     break;
41024             }
41025             
41026             var el = template.overwrite(td, {"text": text});
41027             
41028             var inner = el.getElementsByTagName("span")[0];
41029             
41030             return {"el": el, "inner": inner};
41031             
41032     }
41033         
41034     
41035 });
41036
41037 /**
41038  * @class Roo.TabPanelItem
41039  * @extends Roo.util.Observable
41040  * Represents an individual item (tab plus body) in a TabPanel.
41041  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41042  * @param {String} id The id of this TabPanelItem
41043  * @param {String} text The text for the tab of this TabPanelItem
41044  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41045  */
41046 Roo.bootstrap.panel.TabItem = function(config){
41047     /**
41048      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41049      * @type Roo.TabPanel
41050      */
41051     this.tabPanel = config.panel;
41052     /**
41053      * The id for this TabPanelItem
41054      * @type String
41055      */
41056     this.id = config.id;
41057     /** @private */
41058     this.disabled = false;
41059     /** @private */
41060     this.text = config.text;
41061     /** @private */
41062     this.loaded = false;
41063     this.closable = config.closable;
41064
41065     /**
41066      * The body element for this TabPanelItem.
41067      * @type Roo.Element
41068      */
41069     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41070     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41071     this.bodyEl.setStyle("display", "block");
41072     this.bodyEl.setStyle("zoom", "1");
41073     //this.hideAction();
41074
41075     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41076     /** @private */
41077     this.el = Roo.get(els.el);
41078     this.inner = Roo.get(els.inner, true);
41079      this.textEl = Roo.bootstrap.version == 4 ?
41080         this.el : Roo.get(this.el.dom.firstChild, true);
41081
41082     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41083     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41084
41085     
41086 //    this.el.on("mousedown", this.onTabMouseDown, this);
41087     this.el.on("click", this.onTabClick, this);
41088     /** @private */
41089     if(config.closable){
41090         var c = Roo.get(els.close, true);
41091         c.dom.title = this.closeText;
41092         c.addClassOnOver("close-over");
41093         c.on("click", this.closeClick, this);
41094      }
41095
41096     this.addEvents({
41097          /**
41098          * @event activate
41099          * Fires when this tab becomes the active tab.
41100          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41101          * @param {Roo.TabPanelItem} this
41102          */
41103         "activate": true,
41104         /**
41105          * @event beforeclose
41106          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41107          * @param {Roo.TabPanelItem} this
41108          * @param {Object} e Set cancel to true on this object to cancel the close.
41109          */
41110         "beforeclose": true,
41111         /**
41112          * @event close
41113          * Fires when this tab is closed.
41114          * @param {Roo.TabPanelItem} this
41115          */
41116          "close": true,
41117         /**
41118          * @event deactivate
41119          * Fires when this tab is no longer the active tab.
41120          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41121          * @param {Roo.TabPanelItem} this
41122          */
41123          "deactivate" : true
41124     });
41125     this.hidden = false;
41126
41127     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41128 };
41129
41130 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41131            {
41132     purgeListeners : function(){
41133        Roo.util.Observable.prototype.purgeListeners.call(this);
41134        this.el.removeAllListeners();
41135     },
41136     /**
41137      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41138      */
41139     show : function(){
41140         this.status_node.addClass("active");
41141         this.showAction();
41142         if(Roo.isOpera){
41143             this.tabPanel.stripWrap.repaint();
41144         }
41145         this.fireEvent("activate", this.tabPanel, this);
41146     },
41147
41148     /**
41149      * Returns true if this tab is the active tab.
41150      * @return {Boolean}
41151      */
41152     isActive : function(){
41153         return this.tabPanel.getActiveTab() == this;
41154     },
41155
41156     /**
41157      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41158      */
41159     hide : function(){
41160         this.status_node.removeClass("active");
41161         this.hideAction();
41162         this.fireEvent("deactivate", this.tabPanel, this);
41163     },
41164
41165     hideAction : function(){
41166         this.bodyEl.hide();
41167         this.bodyEl.setStyle("position", "absolute");
41168         this.bodyEl.setLeft("-20000px");
41169         this.bodyEl.setTop("-20000px");
41170     },
41171
41172     showAction : function(){
41173         this.bodyEl.setStyle("position", "relative");
41174         this.bodyEl.setTop("");
41175         this.bodyEl.setLeft("");
41176         this.bodyEl.show();
41177     },
41178
41179     /**
41180      * Set the tooltip for the tab.
41181      * @param {String} tooltip The tab's tooltip
41182      */
41183     setTooltip : function(text){
41184         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41185             this.textEl.dom.qtip = text;
41186             this.textEl.dom.removeAttribute('title');
41187         }else{
41188             this.textEl.dom.title = text;
41189         }
41190     },
41191
41192     onTabClick : function(e){
41193         e.preventDefault();
41194         this.tabPanel.activate(this.id);
41195     },
41196
41197     onTabMouseDown : function(e){
41198         e.preventDefault();
41199         this.tabPanel.activate(this.id);
41200     },
41201 /*
41202     getWidth : function(){
41203         return this.inner.getWidth();
41204     },
41205
41206     setWidth : function(width){
41207         var iwidth = width - this.linode.getPadding("lr");
41208         this.inner.setWidth(iwidth);
41209         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41210         this.linode.setWidth(width);
41211     },
41212 */
41213     /**
41214      * Show or hide the tab
41215      * @param {Boolean} hidden True to hide or false to show.
41216      */
41217     setHidden : function(hidden){
41218         this.hidden = hidden;
41219         this.linode.setStyle("display", hidden ? "none" : "");
41220     },
41221
41222     /**
41223      * Returns true if this tab is "hidden"
41224      * @return {Boolean}
41225      */
41226     isHidden : function(){
41227         return this.hidden;
41228     },
41229
41230     /**
41231      * Returns the text for this tab
41232      * @return {String}
41233      */
41234     getText : function(){
41235         return this.text;
41236     },
41237     /*
41238     autoSize : function(){
41239         //this.el.beginMeasure();
41240         this.textEl.setWidth(1);
41241         /*
41242          *  #2804 [new] Tabs in Roojs
41243          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41244          */
41245         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41246         //this.el.endMeasure();
41247     //},
41248
41249     /**
41250      * Sets the text for the tab (Note: this also sets the tooltip text)
41251      * @param {String} text The tab's text and tooltip
41252      */
41253     setText : function(text){
41254         this.text = text;
41255         this.textEl.update(text);
41256         this.setTooltip(text);
41257         //if(!this.tabPanel.resizeTabs){
41258         //    this.autoSize();
41259         //}
41260     },
41261     /**
41262      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41263      */
41264     activate : function(){
41265         this.tabPanel.activate(this.id);
41266     },
41267
41268     /**
41269      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41270      */
41271     disable : function(){
41272         if(this.tabPanel.active != this){
41273             this.disabled = true;
41274             this.status_node.addClass("disabled");
41275         }
41276     },
41277
41278     /**
41279      * Enables this TabPanelItem if it was previously disabled.
41280      */
41281     enable : function(){
41282         this.disabled = false;
41283         this.status_node.removeClass("disabled");
41284     },
41285
41286     /**
41287      * Sets the content for this TabPanelItem.
41288      * @param {String} content The content
41289      * @param {Boolean} loadScripts true to look for and load scripts
41290      */
41291     setContent : function(content, loadScripts){
41292         this.bodyEl.update(content, loadScripts);
41293     },
41294
41295     /**
41296      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41297      * @return {Roo.UpdateManager} The UpdateManager
41298      */
41299     getUpdateManager : function(){
41300         return this.bodyEl.getUpdateManager();
41301     },
41302
41303     /**
41304      * Set a URL to be used to load the content for this TabPanelItem.
41305      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41306      * @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)
41307      * @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)
41308      * @return {Roo.UpdateManager} The UpdateManager
41309      */
41310     setUrl : function(url, params, loadOnce){
41311         if(this.refreshDelegate){
41312             this.un('activate', this.refreshDelegate);
41313         }
41314         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41315         this.on("activate", this.refreshDelegate);
41316         return this.bodyEl.getUpdateManager();
41317     },
41318
41319     /** @private */
41320     _handleRefresh : function(url, params, loadOnce){
41321         if(!loadOnce || !this.loaded){
41322             var updater = this.bodyEl.getUpdateManager();
41323             updater.update(url, params, this._setLoaded.createDelegate(this));
41324         }
41325     },
41326
41327     /**
41328      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41329      *   Will fail silently if the setUrl method has not been called.
41330      *   This does not activate the panel, just updates its content.
41331      */
41332     refresh : function(){
41333         if(this.refreshDelegate){
41334            this.loaded = false;
41335            this.refreshDelegate();
41336         }
41337     },
41338
41339     /** @private */
41340     _setLoaded : function(){
41341         this.loaded = true;
41342     },
41343
41344     /** @private */
41345     closeClick : function(e){
41346         var o = {};
41347         e.stopEvent();
41348         this.fireEvent("beforeclose", this, o);
41349         if(o.cancel !== true){
41350             this.tabPanel.removeTab(this.id);
41351         }
41352     },
41353     /**
41354      * The text displayed in the tooltip for the close icon.
41355      * @type String
41356      */
41357     closeText : "Close this tab"
41358 });
41359 /**
41360 *    This script refer to:
41361 *    Title: International Telephone Input
41362 *    Author: Jack O'Connor
41363 *    Code version:  v12.1.12
41364 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41365 **/
41366
41367 Roo.bootstrap.PhoneInputData = function() {
41368     var d = [
41369       [
41370         "Afghanistan (‫افغانستان‬‎)",
41371         "af",
41372         "93"
41373       ],
41374       [
41375         "Albania (Shqipëri)",
41376         "al",
41377         "355"
41378       ],
41379       [
41380         "Algeria (‫الجزائر‬‎)",
41381         "dz",
41382         "213"
41383       ],
41384       [
41385         "American Samoa",
41386         "as",
41387         "1684"
41388       ],
41389       [
41390         "Andorra",
41391         "ad",
41392         "376"
41393       ],
41394       [
41395         "Angola",
41396         "ao",
41397         "244"
41398       ],
41399       [
41400         "Anguilla",
41401         "ai",
41402         "1264"
41403       ],
41404       [
41405         "Antigua and Barbuda",
41406         "ag",
41407         "1268"
41408       ],
41409       [
41410         "Argentina",
41411         "ar",
41412         "54"
41413       ],
41414       [
41415         "Armenia (Հայաստան)",
41416         "am",
41417         "374"
41418       ],
41419       [
41420         "Aruba",
41421         "aw",
41422         "297"
41423       ],
41424       [
41425         "Australia",
41426         "au",
41427         "61",
41428         0
41429       ],
41430       [
41431         "Austria (Österreich)",
41432         "at",
41433         "43"
41434       ],
41435       [
41436         "Azerbaijan (Azərbaycan)",
41437         "az",
41438         "994"
41439       ],
41440       [
41441         "Bahamas",
41442         "bs",
41443         "1242"
41444       ],
41445       [
41446         "Bahrain (‫البحرين‬‎)",
41447         "bh",
41448         "973"
41449       ],
41450       [
41451         "Bangladesh (বাংলাদেশ)",
41452         "bd",
41453         "880"
41454       ],
41455       [
41456         "Barbados",
41457         "bb",
41458         "1246"
41459       ],
41460       [
41461         "Belarus (Беларусь)",
41462         "by",
41463         "375"
41464       ],
41465       [
41466         "Belgium (België)",
41467         "be",
41468         "32"
41469       ],
41470       [
41471         "Belize",
41472         "bz",
41473         "501"
41474       ],
41475       [
41476         "Benin (Bénin)",
41477         "bj",
41478         "229"
41479       ],
41480       [
41481         "Bermuda",
41482         "bm",
41483         "1441"
41484       ],
41485       [
41486         "Bhutan (འབྲུག)",
41487         "bt",
41488         "975"
41489       ],
41490       [
41491         "Bolivia",
41492         "bo",
41493         "591"
41494       ],
41495       [
41496         "Bosnia and Herzegovina (Босна и Херцеговина)",
41497         "ba",
41498         "387"
41499       ],
41500       [
41501         "Botswana",
41502         "bw",
41503         "267"
41504       ],
41505       [
41506         "Brazil (Brasil)",
41507         "br",
41508         "55"
41509       ],
41510       [
41511         "British Indian Ocean Territory",
41512         "io",
41513         "246"
41514       ],
41515       [
41516         "British Virgin Islands",
41517         "vg",
41518         "1284"
41519       ],
41520       [
41521         "Brunei",
41522         "bn",
41523         "673"
41524       ],
41525       [
41526         "Bulgaria (България)",
41527         "bg",
41528         "359"
41529       ],
41530       [
41531         "Burkina Faso",
41532         "bf",
41533         "226"
41534       ],
41535       [
41536         "Burundi (Uburundi)",
41537         "bi",
41538         "257"
41539       ],
41540       [
41541         "Cambodia (កម្ពុជា)",
41542         "kh",
41543         "855"
41544       ],
41545       [
41546         "Cameroon (Cameroun)",
41547         "cm",
41548         "237"
41549       ],
41550       [
41551         "Canada",
41552         "ca",
41553         "1",
41554         1,
41555         ["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"]
41556       ],
41557       [
41558         "Cape Verde (Kabu Verdi)",
41559         "cv",
41560         "238"
41561       ],
41562       [
41563         "Caribbean Netherlands",
41564         "bq",
41565         "599",
41566         1
41567       ],
41568       [
41569         "Cayman Islands",
41570         "ky",
41571         "1345"
41572       ],
41573       [
41574         "Central African Republic (République centrafricaine)",
41575         "cf",
41576         "236"
41577       ],
41578       [
41579         "Chad (Tchad)",
41580         "td",
41581         "235"
41582       ],
41583       [
41584         "Chile",
41585         "cl",
41586         "56"
41587       ],
41588       [
41589         "China (中国)",
41590         "cn",
41591         "86"
41592       ],
41593       [
41594         "Christmas Island",
41595         "cx",
41596         "61",
41597         2
41598       ],
41599       [
41600         "Cocos (Keeling) Islands",
41601         "cc",
41602         "61",
41603         1
41604       ],
41605       [
41606         "Colombia",
41607         "co",
41608         "57"
41609       ],
41610       [
41611         "Comoros (‫جزر القمر‬‎)",
41612         "km",
41613         "269"
41614       ],
41615       [
41616         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41617         "cd",
41618         "243"
41619       ],
41620       [
41621         "Congo (Republic) (Congo-Brazzaville)",
41622         "cg",
41623         "242"
41624       ],
41625       [
41626         "Cook Islands",
41627         "ck",
41628         "682"
41629       ],
41630       [
41631         "Costa Rica",
41632         "cr",
41633         "506"
41634       ],
41635       [
41636         "Côte d’Ivoire",
41637         "ci",
41638         "225"
41639       ],
41640       [
41641         "Croatia (Hrvatska)",
41642         "hr",
41643         "385"
41644       ],
41645       [
41646         "Cuba",
41647         "cu",
41648         "53"
41649       ],
41650       [
41651         "Curaçao",
41652         "cw",
41653         "599",
41654         0
41655       ],
41656       [
41657         "Cyprus (Κύπρος)",
41658         "cy",
41659         "357"
41660       ],
41661       [
41662         "Czech Republic (Česká republika)",
41663         "cz",
41664         "420"
41665       ],
41666       [
41667         "Denmark (Danmark)",
41668         "dk",
41669         "45"
41670       ],
41671       [
41672         "Djibouti",
41673         "dj",
41674         "253"
41675       ],
41676       [
41677         "Dominica",
41678         "dm",
41679         "1767"
41680       ],
41681       [
41682         "Dominican Republic (República Dominicana)",
41683         "do",
41684         "1",
41685         2,
41686         ["809", "829", "849"]
41687       ],
41688       [
41689         "Ecuador",
41690         "ec",
41691         "593"
41692       ],
41693       [
41694         "Egypt (‫مصر‬‎)",
41695         "eg",
41696         "20"
41697       ],
41698       [
41699         "El Salvador",
41700         "sv",
41701         "503"
41702       ],
41703       [
41704         "Equatorial Guinea (Guinea Ecuatorial)",
41705         "gq",
41706         "240"
41707       ],
41708       [
41709         "Eritrea",
41710         "er",
41711         "291"
41712       ],
41713       [
41714         "Estonia (Eesti)",
41715         "ee",
41716         "372"
41717       ],
41718       [
41719         "Ethiopia",
41720         "et",
41721         "251"
41722       ],
41723       [
41724         "Falkland Islands (Islas Malvinas)",
41725         "fk",
41726         "500"
41727       ],
41728       [
41729         "Faroe Islands (Føroyar)",
41730         "fo",
41731         "298"
41732       ],
41733       [
41734         "Fiji",
41735         "fj",
41736         "679"
41737       ],
41738       [
41739         "Finland (Suomi)",
41740         "fi",
41741         "358",
41742         0
41743       ],
41744       [
41745         "France",
41746         "fr",
41747         "33"
41748       ],
41749       [
41750         "French Guiana (Guyane française)",
41751         "gf",
41752         "594"
41753       ],
41754       [
41755         "French Polynesia (Polynésie française)",
41756         "pf",
41757         "689"
41758       ],
41759       [
41760         "Gabon",
41761         "ga",
41762         "241"
41763       ],
41764       [
41765         "Gambia",
41766         "gm",
41767         "220"
41768       ],
41769       [
41770         "Georgia (საქართველო)",
41771         "ge",
41772         "995"
41773       ],
41774       [
41775         "Germany (Deutschland)",
41776         "de",
41777         "49"
41778       ],
41779       [
41780         "Ghana (Gaana)",
41781         "gh",
41782         "233"
41783       ],
41784       [
41785         "Gibraltar",
41786         "gi",
41787         "350"
41788       ],
41789       [
41790         "Greece (Ελλάδα)",
41791         "gr",
41792         "30"
41793       ],
41794       [
41795         "Greenland (Kalaallit Nunaat)",
41796         "gl",
41797         "299"
41798       ],
41799       [
41800         "Grenada",
41801         "gd",
41802         "1473"
41803       ],
41804       [
41805         "Guadeloupe",
41806         "gp",
41807         "590",
41808         0
41809       ],
41810       [
41811         "Guam",
41812         "gu",
41813         "1671"
41814       ],
41815       [
41816         "Guatemala",
41817         "gt",
41818         "502"
41819       ],
41820       [
41821         "Guernsey",
41822         "gg",
41823         "44",
41824         1
41825       ],
41826       [
41827         "Guinea (Guinée)",
41828         "gn",
41829         "224"
41830       ],
41831       [
41832         "Guinea-Bissau (Guiné Bissau)",
41833         "gw",
41834         "245"
41835       ],
41836       [
41837         "Guyana",
41838         "gy",
41839         "592"
41840       ],
41841       [
41842         "Haiti",
41843         "ht",
41844         "509"
41845       ],
41846       [
41847         "Honduras",
41848         "hn",
41849         "504"
41850       ],
41851       [
41852         "Hong Kong (香港)",
41853         "hk",
41854         "852"
41855       ],
41856       [
41857         "Hungary (Magyarország)",
41858         "hu",
41859         "36"
41860       ],
41861       [
41862         "Iceland (Ísland)",
41863         "is",
41864         "354"
41865       ],
41866       [
41867         "India (भारत)",
41868         "in",
41869         "91"
41870       ],
41871       [
41872         "Indonesia",
41873         "id",
41874         "62"
41875       ],
41876       [
41877         "Iran (‫ایران‬‎)",
41878         "ir",
41879         "98"
41880       ],
41881       [
41882         "Iraq (‫العراق‬‎)",
41883         "iq",
41884         "964"
41885       ],
41886       [
41887         "Ireland",
41888         "ie",
41889         "353"
41890       ],
41891       [
41892         "Isle of Man",
41893         "im",
41894         "44",
41895         2
41896       ],
41897       [
41898         "Israel (‫ישראל‬‎)",
41899         "il",
41900         "972"
41901       ],
41902       [
41903         "Italy (Italia)",
41904         "it",
41905         "39",
41906         0
41907       ],
41908       [
41909         "Jamaica",
41910         "jm",
41911         "1876"
41912       ],
41913       [
41914         "Japan (日本)",
41915         "jp",
41916         "81"
41917       ],
41918       [
41919         "Jersey",
41920         "je",
41921         "44",
41922         3
41923       ],
41924       [
41925         "Jordan (‫الأردن‬‎)",
41926         "jo",
41927         "962"
41928       ],
41929       [
41930         "Kazakhstan (Казахстан)",
41931         "kz",
41932         "7",
41933         1
41934       ],
41935       [
41936         "Kenya",
41937         "ke",
41938         "254"
41939       ],
41940       [
41941         "Kiribati",
41942         "ki",
41943         "686"
41944       ],
41945       [
41946         "Kosovo",
41947         "xk",
41948         "383"
41949       ],
41950       [
41951         "Kuwait (‫الكويت‬‎)",
41952         "kw",
41953         "965"
41954       ],
41955       [
41956         "Kyrgyzstan (Кыргызстан)",
41957         "kg",
41958         "996"
41959       ],
41960       [
41961         "Laos (ລາວ)",
41962         "la",
41963         "856"
41964       ],
41965       [
41966         "Latvia (Latvija)",
41967         "lv",
41968         "371"
41969       ],
41970       [
41971         "Lebanon (‫لبنان‬‎)",
41972         "lb",
41973         "961"
41974       ],
41975       [
41976         "Lesotho",
41977         "ls",
41978         "266"
41979       ],
41980       [
41981         "Liberia",
41982         "lr",
41983         "231"
41984       ],
41985       [
41986         "Libya (‫ليبيا‬‎)",
41987         "ly",
41988         "218"
41989       ],
41990       [
41991         "Liechtenstein",
41992         "li",
41993         "423"
41994       ],
41995       [
41996         "Lithuania (Lietuva)",
41997         "lt",
41998         "370"
41999       ],
42000       [
42001         "Luxembourg",
42002         "lu",
42003         "352"
42004       ],
42005       [
42006         "Macau (澳門)",
42007         "mo",
42008         "853"
42009       ],
42010       [
42011         "Macedonia (FYROM) (Македонија)",
42012         "mk",
42013         "389"
42014       ],
42015       [
42016         "Madagascar (Madagasikara)",
42017         "mg",
42018         "261"
42019       ],
42020       [
42021         "Malawi",
42022         "mw",
42023         "265"
42024       ],
42025       [
42026         "Malaysia",
42027         "my",
42028         "60"
42029       ],
42030       [
42031         "Maldives",
42032         "mv",
42033         "960"
42034       ],
42035       [
42036         "Mali",
42037         "ml",
42038         "223"
42039       ],
42040       [
42041         "Malta",
42042         "mt",
42043         "356"
42044       ],
42045       [
42046         "Marshall Islands",
42047         "mh",
42048         "692"
42049       ],
42050       [
42051         "Martinique",
42052         "mq",
42053         "596"
42054       ],
42055       [
42056         "Mauritania (‫موريتانيا‬‎)",
42057         "mr",
42058         "222"
42059       ],
42060       [
42061         "Mauritius (Moris)",
42062         "mu",
42063         "230"
42064       ],
42065       [
42066         "Mayotte",
42067         "yt",
42068         "262",
42069         1
42070       ],
42071       [
42072         "Mexico (México)",
42073         "mx",
42074         "52"
42075       ],
42076       [
42077         "Micronesia",
42078         "fm",
42079         "691"
42080       ],
42081       [
42082         "Moldova (Republica Moldova)",
42083         "md",
42084         "373"
42085       ],
42086       [
42087         "Monaco",
42088         "mc",
42089         "377"
42090       ],
42091       [
42092         "Mongolia (Монгол)",
42093         "mn",
42094         "976"
42095       ],
42096       [
42097         "Montenegro (Crna Gora)",
42098         "me",
42099         "382"
42100       ],
42101       [
42102         "Montserrat",
42103         "ms",
42104         "1664"
42105       ],
42106       [
42107         "Morocco (‫المغرب‬‎)",
42108         "ma",
42109         "212",
42110         0
42111       ],
42112       [
42113         "Mozambique (Moçambique)",
42114         "mz",
42115         "258"
42116       ],
42117       [
42118         "Myanmar (Burma) (မြန်မာ)",
42119         "mm",
42120         "95"
42121       ],
42122       [
42123         "Namibia (Namibië)",
42124         "na",
42125         "264"
42126       ],
42127       [
42128         "Nauru",
42129         "nr",
42130         "674"
42131       ],
42132       [
42133         "Nepal (नेपाल)",
42134         "np",
42135         "977"
42136       ],
42137       [
42138         "Netherlands (Nederland)",
42139         "nl",
42140         "31"
42141       ],
42142       [
42143         "New Caledonia (Nouvelle-Calédonie)",
42144         "nc",
42145         "687"
42146       ],
42147       [
42148         "New Zealand",
42149         "nz",
42150         "64"
42151       ],
42152       [
42153         "Nicaragua",
42154         "ni",
42155         "505"
42156       ],
42157       [
42158         "Niger (Nijar)",
42159         "ne",
42160         "227"
42161       ],
42162       [
42163         "Nigeria",
42164         "ng",
42165         "234"
42166       ],
42167       [
42168         "Niue",
42169         "nu",
42170         "683"
42171       ],
42172       [
42173         "Norfolk Island",
42174         "nf",
42175         "672"
42176       ],
42177       [
42178         "North Korea (조선 민주주의 인민 공화국)",
42179         "kp",
42180         "850"
42181       ],
42182       [
42183         "Northern Mariana Islands",
42184         "mp",
42185         "1670"
42186       ],
42187       [
42188         "Norway (Norge)",
42189         "no",
42190         "47",
42191         0
42192       ],
42193       [
42194         "Oman (‫عُمان‬‎)",
42195         "om",
42196         "968"
42197       ],
42198       [
42199         "Pakistan (‫پاکستان‬‎)",
42200         "pk",
42201         "92"
42202       ],
42203       [
42204         "Palau",
42205         "pw",
42206         "680"
42207       ],
42208       [
42209         "Palestine (‫فلسطين‬‎)",
42210         "ps",
42211         "970"
42212       ],
42213       [
42214         "Panama (Panamá)",
42215         "pa",
42216         "507"
42217       ],
42218       [
42219         "Papua New Guinea",
42220         "pg",
42221         "675"
42222       ],
42223       [
42224         "Paraguay",
42225         "py",
42226         "595"
42227       ],
42228       [
42229         "Peru (Perú)",
42230         "pe",
42231         "51"
42232       ],
42233       [
42234         "Philippines",
42235         "ph",
42236         "63"
42237       ],
42238       [
42239         "Poland (Polska)",
42240         "pl",
42241         "48"
42242       ],
42243       [
42244         "Portugal",
42245         "pt",
42246         "351"
42247       ],
42248       [
42249         "Puerto Rico",
42250         "pr",
42251         "1",
42252         3,
42253         ["787", "939"]
42254       ],
42255       [
42256         "Qatar (‫قطر‬‎)",
42257         "qa",
42258         "974"
42259       ],
42260       [
42261         "Réunion (La Réunion)",
42262         "re",
42263         "262",
42264         0
42265       ],
42266       [
42267         "Romania (România)",
42268         "ro",
42269         "40"
42270       ],
42271       [
42272         "Russia (Россия)",
42273         "ru",
42274         "7",
42275         0
42276       ],
42277       [
42278         "Rwanda",
42279         "rw",
42280         "250"
42281       ],
42282       [
42283         "Saint Barthélemy",
42284         "bl",
42285         "590",
42286         1
42287       ],
42288       [
42289         "Saint Helena",
42290         "sh",
42291         "290"
42292       ],
42293       [
42294         "Saint Kitts and Nevis",
42295         "kn",
42296         "1869"
42297       ],
42298       [
42299         "Saint Lucia",
42300         "lc",
42301         "1758"
42302       ],
42303       [
42304         "Saint Martin (Saint-Martin (partie française))",
42305         "mf",
42306         "590",
42307         2
42308       ],
42309       [
42310         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42311         "pm",
42312         "508"
42313       ],
42314       [
42315         "Saint Vincent and the Grenadines",
42316         "vc",
42317         "1784"
42318       ],
42319       [
42320         "Samoa",
42321         "ws",
42322         "685"
42323       ],
42324       [
42325         "San Marino",
42326         "sm",
42327         "378"
42328       ],
42329       [
42330         "São Tomé and Príncipe (São Tomé e Príncipe)",
42331         "st",
42332         "239"
42333       ],
42334       [
42335         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42336         "sa",
42337         "966"
42338       ],
42339       [
42340         "Senegal (Sénégal)",
42341         "sn",
42342         "221"
42343       ],
42344       [
42345         "Serbia (Србија)",
42346         "rs",
42347         "381"
42348       ],
42349       [
42350         "Seychelles",
42351         "sc",
42352         "248"
42353       ],
42354       [
42355         "Sierra Leone",
42356         "sl",
42357         "232"
42358       ],
42359       [
42360         "Singapore",
42361         "sg",
42362         "65"
42363       ],
42364       [
42365         "Sint Maarten",
42366         "sx",
42367         "1721"
42368       ],
42369       [
42370         "Slovakia (Slovensko)",
42371         "sk",
42372         "421"
42373       ],
42374       [
42375         "Slovenia (Slovenija)",
42376         "si",
42377         "386"
42378       ],
42379       [
42380         "Solomon Islands",
42381         "sb",
42382         "677"
42383       ],
42384       [
42385         "Somalia (Soomaaliya)",
42386         "so",
42387         "252"
42388       ],
42389       [
42390         "South Africa",
42391         "za",
42392         "27"
42393       ],
42394       [
42395         "South Korea (대한민국)",
42396         "kr",
42397         "82"
42398       ],
42399       [
42400         "South Sudan (‫جنوب السودان‬‎)",
42401         "ss",
42402         "211"
42403       ],
42404       [
42405         "Spain (España)",
42406         "es",
42407         "34"
42408       ],
42409       [
42410         "Sri Lanka (ශ්‍රී ලංකාව)",
42411         "lk",
42412         "94"
42413       ],
42414       [
42415         "Sudan (‫السودان‬‎)",
42416         "sd",
42417         "249"
42418       ],
42419       [
42420         "Suriname",
42421         "sr",
42422         "597"
42423       ],
42424       [
42425         "Svalbard and Jan Mayen",
42426         "sj",
42427         "47",
42428         1
42429       ],
42430       [
42431         "Swaziland",
42432         "sz",
42433         "268"
42434       ],
42435       [
42436         "Sweden (Sverige)",
42437         "se",
42438         "46"
42439       ],
42440       [
42441         "Switzerland (Schweiz)",
42442         "ch",
42443         "41"
42444       ],
42445       [
42446         "Syria (‫سوريا‬‎)",
42447         "sy",
42448         "963"
42449       ],
42450       [
42451         "Taiwan (台灣)",
42452         "tw",
42453         "886"
42454       ],
42455       [
42456         "Tajikistan",
42457         "tj",
42458         "992"
42459       ],
42460       [
42461         "Tanzania",
42462         "tz",
42463         "255"
42464       ],
42465       [
42466         "Thailand (ไทย)",
42467         "th",
42468         "66"
42469       ],
42470       [
42471         "Timor-Leste",
42472         "tl",
42473         "670"
42474       ],
42475       [
42476         "Togo",
42477         "tg",
42478         "228"
42479       ],
42480       [
42481         "Tokelau",
42482         "tk",
42483         "690"
42484       ],
42485       [
42486         "Tonga",
42487         "to",
42488         "676"
42489       ],
42490       [
42491         "Trinidad and Tobago",
42492         "tt",
42493         "1868"
42494       ],
42495       [
42496         "Tunisia (‫تونس‬‎)",
42497         "tn",
42498         "216"
42499       ],
42500       [
42501         "Turkey (Türkiye)",
42502         "tr",
42503         "90"
42504       ],
42505       [
42506         "Turkmenistan",
42507         "tm",
42508         "993"
42509       ],
42510       [
42511         "Turks and Caicos Islands",
42512         "tc",
42513         "1649"
42514       ],
42515       [
42516         "Tuvalu",
42517         "tv",
42518         "688"
42519       ],
42520       [
42521         "U.S. Virgin Islands",
42522         "vi",
42523         "1340"
42524       ],
42525       [
42526         "Uganda",
42527         "ug",
42528         "256"
42529       ],
42530       [
42531         "Ukraine (Україна)",
42532         "ua",
42533         "380"
42534       ],
42535       [
42536         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42537         "ae",
42538         "971"
42539       ],
42540       [
42541         "United Kingdom",
42542         "gb",
42543         "44",
42544         0
42545       ],
42546       [
42547         "United States",
42548         "us",
42549         "1",
42550         0
42551       ],
42552       [
42553         "Uruguay",
42554         "uy",
42555         "598"
42556       ],
42557       [
42558         "Uzbekistan (Oʻzbekiston)",
42559         "uz",
42560         "998"
42561       ],
42562       [
42563         "Vanuatu",
42564         "vu",
42565         "678"
42566       ],
42567       [
42568         "Vatican City (Città del Vaticano)",
42569         "va",
42570         "39",
42571         1
42572       ],
42573       [
42574         "Venezuela",
42575         "ve",
42576         "58"
42577       ],
42578       [
42579         "Vietnam (Việt Nam)",
42580         "vn",
42581         "84"
42582       ],
42583       [
42584         "Wallis and Futuna (Wallis-et-Futuna)",
42585         "wf",
42586         "681"
42587       ],
42588       [
42589         "Western Sahara (‫الصحراء الغربية‬‎)",
42590         "eh",
42591         "212",
42592         1
42593       ],
42594       [
42595         "Yemen (‫اليمن‬‎)",
42596         "ye",
42597         "967"
42598       ],
42599       [
42600         "Zambia",
42601         "zm",
42602         "260"
42603       ],
42604       [
42605         "Zimbabwe",
42606         "zw",
42607         "263"
42608       ],
42609       [
42610         "Åland Islands",
42611         "ax",
42612         "358",
42613         1
42614       ]
42615   ];
42616   
42617   return d;
42618 }/**
42619 *    This script refer to:
42620 *    Title: International Telephone Input
42621 *    Author: Jack O'Connor
42622 *    Code version:  v12.1.12
42623 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42624 **/
42625
42626 /**
42627  * @class Roo.bootstrap.PhoneInput
42628  * @extends Roo.bootstrap.TriggerField
42629  * An input with International dial-code selection
42630  
42631  * @cfg {String} defaultDialCode default '+852'
42632  * @cfg {Array} preferedCountries default []
42633   
42634  * @constructor
42635  * Create a new PhoneInput.
42636  * @param {Object} config Configuration options
42637  */
42638
42639 Roo.bootstrap.PhoneInput = function(config) {
42640     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42641 };
42642
42643 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42644         
42645         listWidth: undefined,
42646         
42647         selectedClass: 'active',
42648         
42649         invalidClass : "has-warning",
42650         
42651         validClass: 'has-success',
42652         
42653         allowed: '0123456789',
42654         
42655         max_length: 15,
42656         
42657         /**
42658          * @cfg {String} defaultDialCode The default dial code when initializing the input
42659          */
42660         defaultDialCode: '+852',
42661         
42662         /**
42663          * @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
42664          */
42665         preferedCountries: false,
42666         
42667         getAutoCreate : function()
42668         {
42669             var data = Roo.bootstrap.PhoneInputData();
42670             var align = this.labelAlign || this.parentLabelAlign();
42671             var id = Roo.id();
42672             
42673             this.allCountries = [];
42674             this.dialCodeMapping = [];
42675             
42676             for (var i = 0; i < data.length; i++) {
42677               var c = data[i];
42678               this.allCountries[i] = {
42679                 name: c[0],
42680                 iso2: c[1],
42681                 dialCode: c[2],
42682                 priority: c[3] || 0,
42683                 areaCodes: c[4] || null
42684               };
42685               this.dialCodeMapping[c[2]] = {
42686                   name: c[0],
42687                   iso2: c[1],
42688                   priority: c[3] || 0,
42689                   areaCodes: c[4] || null
42690               };
42691             }
42692             
42693             var cfg = {
42694                 cls: 'form-group',
42695                 cn: []
42696             };
42697             
42698             var input =  {
42699                 tag: 'input',
42700                 id : id,
42701                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42702                 maxlength: this.max_length,
42703                 cls : 'form-control tel-input',
42704                 autocomplete: 'new-password'
42705             };
42706             
42707             var hiddenInput = {
42708                 tag: 'input',
42709                 type: 'hidden',
42710                 cls: 'hidden-tel-input'
42711             };
42712             
42713             if (this.name) {
42714                 hiddenInput.name = this.name;
42715             }
42716             
42717             if (this.disabled) {
42718                 input.disabled = true;
42719             }
42720             
42721             var flag_container = {
42722                 tag: 'div',
42723                 cls: 'flag-box',
42724                 cn: [
42725                     {
42726                         tag: 'div',
42727                         cls: 'flag'
42728                     },
42729                     {
42730                         tag: 'div',
42731                         cls: 'caret'
42732                     }
42733                 ]
42734             };
42735             
42736             var box = {
42737                 tag: 'div',
42738                 cls: this.hasFeedback ? 'has-feedback' : '',
42739                 cn: [
42740                     hiddenInput,
42741                     input,
42742                     {
42743                         tag: 'input',
42744                         cls: 'dial-code-holder',
42745                         disabled: true
42746                     }
42747                 ]
42748             };
42749             
42750             var container = {
42751                 cls: 'roo-select2-container input-group',
42752                 cn: [
42753                     flag_container,
42754                     box
42755                 ]
42756             };
42757             
42758             if (this.fieldLabel.length) {
42759                 var indicator = {
42760                     tag: 'i',
42761                     tooltip: 'This field is required'
42762                 };
42763                 
42764                 var label = {
42765                     tag: 'label',
42766                     'for':  id,
42767                     cls: 'control-label',
42768                     cn: []
42769                 };
42770                 
42771                 var label_text = {
42772                     tag: 'span',
42773                     html: this.fieldLabel
42774                 };
42775                 
42776                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42777                 label.cn = [
42778                     indicator,
42779                     label_text
42780                 ];
42781                 
42782                 if(this.indicatorpos == 'right') {
42783                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42784                     label.cn = [
42785                         label_text,
42786                         indicator
42787                     ];
42788                 }
42789                 
42790                 if(align == 'left') {
42791                     container = {
42792                         tag: 'div',
42793                         cn: [
42794                             container
42795                         ]
42796                     };
42797                     
42798                     if(this.labelWidth > 12){
42799                         label.style = "width: " + this.labelWidth + 'px';
42800                     }
42801                     if(this.labelWidth < 13 && this.labelmd == 0){
42802                         this.labelmd = this.labelWidth;
42803                     }
42804                     if(this.labellg > 0){
42805                         label.cls += ' col-lg-' + this.labellg;
42806                         input.cls += ' col-lg-' + (12 - this.labellg);
42807                     }
42808                     if(this.labelmd > 0){
42809                         label.cls += ' col-md-' + this.labelmd;
42810                         container.cls += ' col-md-' + (12 - this.labelmd);
42811                     }
42812                     if(this.labelsm > 0){
42813                         label.cls += ' col-sm-' + this.labelsm;
42814                         container.cls += ' col-sm-' + (12 - this.labelsm);
42815                     }
42816                     if(this.labelxs > 0){
42817                         label.cls += ' col-xs-' + this.labelxs;
42818                         container.cls += ' col-xs-' + (12 - this.labelxs);
42819                     }
42820                 }
42821             }
42822             
42823             cfg.cn = [
42824                 label,
42825                 container
42826             ];
42827             
42828             var settings = this;
42829             
42830             ['xs','sm','md','lg'].map(function(size){
42831                 if (settings[size]) {
42832                     cfg.cls += ' col-' + size + '-' + settings[size];
42833                 }
42834             });
42835             
42836             this.store = new Roo.data.Store({
42837                 proxy : new Roo.data.MemoryProxy({}),
42838                 reader : new Roo.data.JsonReader({
42839                     fields : [
42840                         {
42841                             'name' : 'name',
42842                             'type' : 'string'
42843                         },
42844                         {
42845                             'name' : 'iso2',
42846                             'type' : 'string'
42847                         },
42848                         {
42849                             'name' : 'dialCode',
42850                             'type' : 'string'
42851                         },
42852                         {
42853                             'name' : 'priority',
42854                             'type' : 'string'
42855                         },
42856                         {
42857                             'name' : 'areaCodes',
42858                             'type' : 'string'
42859                         }
42860                     ]
42861                 })
42862             });
42863             
42864             if(!this.preferedCountries) {
42865                 this.preferedCountries = [
42866                     'hk',
42867                     'gb',
42868                     'us'
42869                 ];
42870             }
42871             
42872             var p = this.preferedCountries.reverse();
42873             
42874             if(p) {
42875                 for (var i = 0; i < p.length; i++) {
42876                     for (var j = 0; j < this.allCountries.length; j++) {
42877                         if(this.allCountries[j].iso2 == p[i]) {
42878                             var t = this.allCountries[j];
42879                             this.allCountries.splice(j,1);
42880                             this.allCountries.unshift(t);
42881                         }
42882                     } 
42883                 }
42884             }
42885             
42886             this.store.proxy.data = {
42887                 success: true,
42888                 data: this.allCountries
42889             };
42890             
42891             return cfg;
42892         },
42893         
42894         initEvents : function()
42895         {
42896             this.createList();
42897             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42898             
42899             this.indicator = this.indicatorEl();
42900             this.flag = this.flagEl();
42901             this.dialCodeHolder = this.dialCodeHolderEl();
42902             
42903             this.trigger = this.el.select('div.flag-box',true).first();
42904             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42905             
42906             var _this = this;
42907             
42908             (function(){
42909                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42910                 _this.list.setWidth(lw);
42911             }).defer(100);
42912             
42913             this.list.on('mouseover', this.onViewOver, this);
42914             this.list.on('mousemove', this.onViewMove, this);
42915             this.inputEl().on("keyup", this.onKeyUp, this);
42916             this.inputEl().on("keypress", this.onKeyPress, this);
42917             
42918             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42919
42920             this.view = new Roo.View(this.list, this.tpl, {
42921                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42922             });
42923             
42924             this.view.on('click', this.onViewClick, this);
42925             this.setValue(this.defaultDialCode);
42926         },
42927         
42928         onTriggerClick : function(e)
42929         {
42930             Roo.log('trigger click');
42931             if(this.disabled){
42932                 return;
42933             }
42934             
42935             if(this.isExpanded()){
42936                 this.collapse();
42937                 this.hasFocus = false;
42938             }else {
42939                 this.store.load({});
42940                 this.hasFocus = true;
42941                 this.expand();
42942             }
42943         },
42944         
42945         isExpanded : function()
42946         {
42947             return this.list.isVisible();
42948         },
42949         
42950         collapse : function()
42951         {
42952             if(!this.isExpanded()){
42953                 return;
42954             }
42955             this.list.hide();
42956             Roo.get(document).un('mousedown', this.collapseIf, this);
42957             Roo.get(document).un('mousewheel', this.collapseIf, this);
42958             this.fireEvent('collapse', this);
42959             this.validate();
42960         },
42961         
42962         expand : function()
42963         {
42964             Roo.log('expand');
42965
42966             if(this.isExpanded() || !this.hasFocus){
42967                 return;
42968             }
42969             
42970             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42971             this.list.setWidth(lw);
42972             
42973             this.list.show();
42974             this.restrictHeight();
42975             
42976             Roo.get(document).on('mousedown', this.collapseIf, this);
42977             Roo.get(document).on('mousewheel', this.collapseIf, this);
42978             
42979             this.fireEvent('expand', this);
42980         },
42981         
42982         restrictHeight : function()
42983         {
42984             this.list.alignTo(this.inputEl(), this.listAlign);
42985             this.list.alignTo(this.inputEl(), this.listAlign);
42986         },
42987         
42988         onViewOver : function(e, t)
42989         {
42990             if(this.inKeyMode){
42991                 return;
42992             }
42993             var item = this.view.findItemFromChild(t);
42994             
42995             if(item){
42996                 var index = this.view.indexOf(item);
42997                 this.select(index, false);
42998             }
42999         },
43000
43001         // private
43002         onViewClick : function(view, doFocus, el, e)
43003         {
43004             var index = this.view.getSelectedIndexes()[0];
43005             
43006             var r = this.store.getAt(index);
43007             
43008             if(r){
43009                 this.onSelect(r, index);
43010             }
43011             if(doFocus !== false && !this.blockFocus){
43012                 this.inputEl().focus();
43013             }
43014         },
43015         
43016         onViewMove : function(e, t)
43017         {
43018             this.inKeyMode = false;
43019         },
43020         
43021         select : function(index, scrollIntoView)
43022         {
43023             this.selectedIndex = index;
43024             this.view.select(index);
43025             if(scrollIntoView !== false){
43026                 var el = this.view.getNode(index);
43027                 if(el){
43028                     this.list.scrollChildIntoView(el, false);
43029                 }
43030             }
43031         },
43032         
43033         createList : function()
43034         {
43035             this.list = Roo.get(document.body).createChild({
43036                 tag: 'ul',
43037                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43038                 style: 'display:none'
43039             });
43040             
43041             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43042         },
43043         
43044         collapseIf : function(e)
43045         {
43046             var in_combo  = e.within(this.el);
43047             var in_list =  e.within(this.list);
43048             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43049             
43050             if (in_combo || in_list || is_list) {
43051                 return;
43052             }
43053             this.collapse();
43054         },
43055         
43056         onSelect : function(record, index)
43057         {
43058             if(this.fireEvent('beforeselect', this, record, index) !== false){
43059                 
43060                 this.setFlagClass(record.data.iso2);
43061                 this.setDialCode(record.data.dialCode);
43062                 this.hasFocus = false;
43063                 this.collapse();
43064                 this.fireEvent('select', this, record, index);
43065             }
43066         },
43067         
43068         flagEl : function()
43069         {
43070             var flag = this.el.select('div.flag',true).first();
43071             if(!flag){
43072                 return false;
43073             }
43074             return flag;
43075         },
43076         
43077         dialCodeHolderEl : function()
43078         {
43079             var d = this.el.select('input.dial-code-holder',true).first();
43080             if(!d){
43081                 return false;
43082             }
43083             return d;
43084         },
43085         
43086         setDialCode : function(v)
43087         {
43088             this.dialCodeHolder.dom.value = '+'+v;
43089         },
43090         
43091         setFlagClass : function(n)
43092         {
43093             this.flag.dom.className = 'flag '+n;
43094         },
43095         
43096         getValue : function()
43097         {
43098             var v = this.inputEl().getValue();
43099             if(this.dialCodeHolder) {
43100                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43101             }
43102             return v;
43103         },
43104         
43105         setValue : function(v)
43106         {
43107             var d = this.getDialCode(v);
43108             
43109             //invalid dial code
43110             if(v.length == 0 || !d || d.length == 0) {
43111                 if(this.rendered){
43112                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43113                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43114                 }
43115                 return;
43116             }
43117             
43118             //valid dial code
43119             this.setFlagClass(this.dialCodeMapping[d].iso2);
43120             this.setDialCode(d);
43121             this.inputEl().dom.value = v.replace('+'+d,'');
43122             this.hiddenEl().dom.value = this.getValue();
43123             
43124             this.validate();
43125         },
43126         
43127         getDialCode : function(v)
43128         {
43129             v = v ||  '';
43130             
43131             if (v.length == 0) {
43132                 return this.dialCodeHolder.dom.value;
43133             }
43134             
43135             var dialCode = "";
43136             if (v.charAt(0) != "+") {
43137                 return false;
43138             }
43139             var numericChars = "";
43140             for (var i = 1; i < v.length; i++) {
43141               var c = v.charAt(i);
43142               if (!isNaN(c)) {
43143                 numericChars += c;
43144                 if (this.dialCodeMapping[numericChars]) {
43145                   dialCode = v.substr(1, i);
43146                 }
43147                 if (numericChars.length == 4) {
43148                   break;
43149                 }
43150               }
43151             }
43152             return dialCode;
43153         },
43154         
43155         reset : function()
43156         {
43157             this.setValue(this.defaultDialCode);
43158             this.validate();
43159         },
43160         
43161         hiddenEl : function()
43162         {
43163             return this.el.select('input.hidden-tel-input',true).first();
43164         },
43165         
43166         // after setting val
43167         onKeyUp : function(e){
43168             this.setValue(this.getValue());
43169         },
43170         
43171         onKeyPress : function(e){
43172             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43173                 e.stopEvent();
43174             }
43175         }
43176         
43177 });
43178 /**
43179  * @class Roo.bootstrap.MoneyField
43180  * @extends Roo.bootstrap.ComboBox
43181  * Bootstrap MoneyField class
43182  * 
43183  * @constructor
43184  * Create a new MoneyField.
43185  * @param {Object} config Configuration options
43186  */
43187
43188 Roo.bootstrap.MoneyField = function(config) {
43189     
43190     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43191     
43192 };
43193
43194 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43195     
43196     /**
43197      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43198      */
43199     allowDecimals : true,
43200     /**
43201      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43202      */
43203     decimalSeparator : ".",
43204     /**
43205      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43206      */
43207     decimalPrecision : 0,
43208     /**
43209      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43210      */
43211     allowNegative : true,
43212     /**
43213      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43214      */
43215     allowZero: true,
43216     /**
43217      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43218      */
43219     minValue : Number.NEGATIVE_INFINITY,
43220     /**
43221      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43222      */
43223     maxValue : Number.MAX_VALUE,
43224     /**
43225      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43226      */
43227     minText : "The minimum value for this field is {0}",
43228     /**
43229      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43230      */
43231     maxText : "The maximum value for this field is {0}",
43232     /**
43233      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43234      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43235      */
43236     nanText : "{0} is not a valid number",
43237     /**
43238      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43239      */
43240     castInt : true,
43241     /**
43242      * @cfg {String} defaults currency of the MoneyField
43243      * value should be in lkey
43244      */
43245     defaultCurrency : false,
43246     /**
43247      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43248      */
43249     thousandsDelimiter : false,
43250     /**
43251      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43252      */
43253     max_length: false,
43254     
43255     inputlg : 9,
43256     inputmd : 9,
43257     inputsm : 9,
43258     inputxs : 6,
43259     
43260     store : false,
43261     
43262     getAutoCreate : function()
43263     {
43264         var align = this.labelAlign || this.parentLabelAlign();
43265         
43266         var id = Roo.id();
43267
43268         var cfg = {
43269             cls: 'form-group',
43270             cn: []
43271         };
43272
43273         var input =  {
43274             tag: 'input',
43275             id : id,
43276             cls : 'form-control roo-money-amount-input',
43277             autocomplete: 'new-password'
43278         };
43279         
43280         var hiddenInput = {
43281             tag: 'input',
43282             type: 'hidden',
43283             id: Roo.id(),
43284             cls: 'hidden-number-input'
43285         };
43286         
43287         if(this.max_length) {
43288             input.maxlength = this.max_length; 
43289         }
43290         
43291         if (this.name) {
43292             hiddenInput.name = this.name;
43293         }
43294
43295         if (this.disabled) {
43296             input.disabled = true;
43297         }
43298
43299         var clg = 12 - this.inputlg;
43300         var cmd = 12 - this.inputmd;
43301         var csm = 12 - this.inputsm;
43302         var cxs = 12 - this.inputxs;
43303         
43304         var container = {
43305             tag : 'div',
43306             cls : 'row roo-money-field',
43307             cn : [
43308                 {
43309                     tag : 'div',
43310                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43311                     cn : [
43312                         {
43313                             tag : 'div',
43314                             cls: 'roo-select2-container input-group',
43315                             cn: [
43316                                 {
43317                                     tag : 'input',
43318                                     cls : 'form-control roo-money-currency-input',
43319                                     autocomplete: 'new-password',
43320                                     readOnly : 1,
43321                                     name : this.currencyName
43322                                 },
43323                                 {
43324                                     tag :'span',
43325                                     cls : 'input-group-addon',
43326                                     cn : [
43327                                         {
43328                                             tag: 'span',
43329                                             cls: 'caret'
43330                                         }
43331                                     ]
43332                                 }
43333                             ]
43334                         }
43335                     ]
43336                 },
43337                 {
43338                     tag : 'div',
43339                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43340                     cn : [
43341                         {
43342                             tag: 'div',
43343                             cls: this.hasFeedback ? 'has-feedback' : '',
43344                             cn: [
43345                                 input
43346                             ]
43347                         }
43348                     ]
43349                 }
43350             ]
43351             
43352         };
43353         
43354         if (this.fieldLabel.length) {
43355             var indicator = {
43356                 tag: 'i',
43357                 tooltip: 'This field is required'
43358             };
43359
43360             var label = {
43361                 tag: 'label',
43362                 'for':  id,
43363                 cls: 'control-label',
43364                 cn: []
43365             };
43366
43367             var label_text = {
43368                 tag: 'span',
43369                 html: this.fieldLabel
43370             };
43371
43372             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43373             label.cn = [
43374                 indicator,
43375                 label_text
43376             ];
43377
43378             if(this.indicatorpos == 'right') {
43379                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43380                 label.cn = [
43381                     label_text,
43382                     indicator
43383                 ];
43384             }
43385
43386             if(align == 'left') {
43387                 container = {
43388                     tag: 'div',
43389                     cn: [
43390                         container
43391                     ]
43392                 };
43393
43394                 if(this.labelWidth > 12){
43395                     label.style = "width: " + this.labelWidth + 'px';
43396                 }
43397                 if(this.labelWidth < 13 && this.labelmd == 0){
43398                     this.labelmd = this.labelWidth;
43399                 }
43400                 if(this.labellg > 0){
43401                     label.cls += ' col-lg-' + this.labellg;
43402                     input.cls += ' col-lg-' + (12 - this.labellg);
43403                 }
43404                 if(this.labelmd > 0){
43405                     label.cls += ' col-md-' + this.labelmd;
43406                     container.cls += ' col-md-' + (12 - this.labelmd);
43407                 }
43408                 if(this.labelsm > 0){
43409                     label.cls += ' col-sm-' + this.labelsm;
43410                     container.cls += ' col-sm-' + (12 - this.labelsm);
43411                 }
43412                 if(this.labelxs > 0){
43413                     label.cls += ' col-xs-' + this.labelxs;
43414                     container.cls += ' col-xs-' + (12 - this.labelxs);
43415                 }
43416             }
43417         }
43418
43419         cfg.cn = [
43420             label,
43421             container,
43422             hiddenInput
43423         ];
43424         
43425         var settings = this;
43426
43427         ['xs','sm','md','lg'].map(function(size){
43428             if (settings[size]) {
43429                 cfg.cls += ' col-' + size + '-' + settings[size];
43430             }
43431         });
43432         
43433         return cfg;
43434     },
43435     
43436     initEvents : function()
43437     {
43438         this.indicator = this.indicatorEl();
43439         
43440         this.initCurrencyEvent();
43441         
43442         this.initNumberEvent();
43443     },
43444     
43445     initCurrencyEvent : function()
43446     {
43447         if (!this.store) {
43448             throw "can not find store for combo";
43449         }
43450         
43451         this.store = Roo.factory(this.store, Roo.data);
43452         this.store.parent = this;
43453         
43454         this.createList();
43455         
43456         this.triggerEl = this.el.select('.input-group-addon', true).first();
43457         
43458         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43459         
43460         var _this = this;
43461         
43462         (function(){
43463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43464             _this.list.setWidth(lw);
43465         }).defer(100);
43466         
43467         this.list.on('mouseover', this.onViewOver, this);
43468         this.list.on('mousemove', this.onViewMove, this);
43469         this.list.on('scroll', this.onViewScroll, this);
43470         
43471         if(!this.tpl){
43472             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43473         }
43474         
43475         this.view = new Roo.View(this.list, this.tpl, {
43476             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43477         });
43478         
43479         this.view.on('click', this.onViewClick, this);
43480         
43481         this.store.on('beforeload', this.onBeforeLoad, this);
43482         this.store.on('load', this.onLoad, this);
43483         this.store.on('loadexception', this.onLoadException, this);
43484         
43485         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43486             "up" : function(e){
43487                 this.inKeyMode = true;
43488                 this.selectPrev();
43489             },
43490
43491             "down" : function(e){
43492                 if(!this.isExpanded()){
43493                     this.onTriggerClick();
43494                 }else{
43495                     this.inKeyMode = true;
43496                     this.selectNext();
43497                 }
43498             },
43499
43500             "enter" : function(e){
43501                 this.collapse();
43502                 
43503                 if(this.fireEvent("specialkey", this, e)){
43504                     this.onViewClick(false);
43505                 }
43506                 
43507                 return true;
43508             },
43509
43510             "esc" : function(e){
43511                 this.collapse();
43512             },
43513
43514             "tab" : function(e){
43515                 this.collapse();
43516                 
43517                 if(this.fireEvent("specialkey", this, e)){
43518                     this.onViewClick(false);
43519                 }
43520                 
43521                 return true;
43522             },
43523
43524             scope : this,
43525
43526             doRelay : function(foo, bar, hname){
43527                 if(hname == 'down' || this.scope.isExpanded()){
43528                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43529                 }
43530                 return true;
43531             },
43532
43533             forceKeyDown: true
43534         });
43535         
43536         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43537         
43538     },
43539     
43540     initNumberEvent : function(e)
43541     {
43542         this.inputEl().on("keydown" , this.fireKey,  this);
43543         this.inputEl().on("focus", this.onFocus,  this);
43544         this.inputEl().on("blur", this.onBlur,  this);
43545         
43546         this.inputEl().relayEvent('keyup', this);
43547         
43548         if(this.indicator){
43549             this.indicator.addClass('invisible');
43550         }
43551  
43552         this.originalValue = this.getValue();
43553         
43554         if(this.validationEvent == 'keyup'){
43555             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43556             this.inputEl().on('keyup', this.filterValidation, this);
43557         }
43558         else if(this.validationEvent !== false){
43559             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43560         }
43561         
43562         if(this.selectOnFocus){
43563             this.on("focus", this.preFocus, this);
43564             
43565         }
43566         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43567             this.inputEl().on("keypress", this.filterKeys, this);
43568         } else {
43569             this.inputEl().relayEvent('keypress', this);
43570         }
43571         
43572         var allowed = "0123456789";
43573         
43574         if(this.allowDecimals){
43575             allowed += this.decimalSeparator;
43576         }
43577         
43578         if(this.allowNegative){
43579             allowed += "-";
43580         }
43581         
43582         if(this.thousandsDelimiter) {
43583             allowed += ",";
43584         }
43585         
43586         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43587         
43588         var keyPress = function(e){
43589             
43590             var k = e.getKey();
43591             
43592             var c = e.getCharCode();
43593             
43594             if(
43595                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43596                     allowed.indexOf(String.fromCharCode(c)) === -1
43597             ){
43598                 e.stopEvent();
43599                 return;
43600             }
43601             
43602             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43603                 return;
43604             }
43605             
43606             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43607                 e.stopEvent();
43608             }
43609         };
43610         
43611         this.inputEl().on("keypress", keyPress, this);
43612         
43613     },
43614     
43615     onTriggerClick : function(e)
43616     {   
43617         if(this.disabled){
43618             return;
43619         }
43620         
43621         this.page = 0;
43622         this.loadNext = false;
43623         
43624         if(this.isExpanded()){
43625             this.collapse();
43626             return;
43627         }
43628         
43629         this.hasFocus = true;
43630         
43631         if(this.triggerAction == 'all') {
43632             this.doQuery(this.allQuery, true);
43633             return;
43634         }
43635         
43636         this.doQuery(this.getRawValue());
43637     },
43638     
43639     getCurrency : function()
43640     {   
43641         var v = this.currencyEl().getValue();
43642         
43643         return v;
43644     },
43645     
43646     restrictHeight : function()
43647     {
43648         this.list.alignTo(this.currencyEl(), this.listAlign);
43649         this.list.alignTo(this.currencyEl(), this.listAlign);
43650     },
43651     
43652     onViewClick : function(view, doFocus, el, e)
43653     {
43654         var index = this.view.getSelectedIndexes()[0];
43655         
43656         var r = this.store.getAt(index);
43657         
43658         if(r){
43659             this.onSelect(r, index);
43660         }
43661     },
43662     
43663     onSelect : function(record, index){
43664         
43665         if(this.fireEvent('beforeselect', this, record, index) !== false){
43666         
43667             this.setFromCurrencyData(index > -1 ? record.data : false);
43668             
43669             this.collapse();
43670             
43671             this.fireEvent('select', this, record, index);
43672         }
43673     },
43674     
43675     setFromCurrencyData : function(o)
43676     {
43677         var currency = '';
43678         
43679         this.lastCurrency = o;
43680         
43681         if (this.currencyField) {
43682             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43683         } else {
43684             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43685         }
43686         
43687         this.lastSelectionText = currency;
43688         
43689         //setting default currency
43690         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43691             this.setCurrency(this.defaultCurrency);
43692             return;
43693         }
43694         
43695         this.setCurrency(currency);
43696     },
43697     
43698     setFromData : function(o)
43699     {
43700         var c = {};
43701         
43702         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43703         
43704         this.setFromCurrencyData(c);
43705         
43706         var value = '';
43707         
43708         if (this.name) {
43709             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43710         } else {
43711             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43712         }
43713         
43714         this.setValue(value);
43715         
43716     },
43717     
43718     setCurrency : function(v)
43719     {   
43720         this.currencyValue = v;
43721         
43722         if(this.rendered){
43723             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43724             this.validate();
43725         }
43726     },
43727     
43728     setValue : function(v)
43729     {
43730         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43731         
43732         this.value = v;
43733         
43734         if(this.rendered){
43735             
43736             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43737             
43738             this.inputEl().dom.value = (v == '') ? '' :
43739                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43740             
43741             if(!this.allowZero && v === '0') {
43742                 this.hiddenEl().dom.value = '';
43743                 this.inputEl().dom.value = '';
43744             }
43745             
43746             this.validate();
43747         }
43748     },
43749     
43750     getRawValue : function()
43751     {
43752         var v = this.inputEl().getValue();
43753         
43754         return v;
43755     },
43756     
43757     getValue : function()
43758     {
43759         return this.fixPrecision(this.parseValue(this.getRawValue()));
43760     },
43761     
43762     parseValue : function(value)
43763     {
43764         if(this.thousandsDelimiter) {
43765             value += "";
43766             r = new RegExp(",", "g");
43767             value = value.replace(r, "");
43768         }
43769         
43770         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43771         return isNaN(value) ? '' : value;
43772         
43773     },
43774     
43775     fixPrecision : function(value)
43776     {
43777         if(this.thousandsDelimiter) {
43778             value += "";
43779             r = new RegExp(",", "g");
43780             value = value.replace(r, "");
43781         }
43782         
43783         var nan = isNaN(value);
43784         
43785         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43786             return nan ? '' : value;
43787         }
43788         return parseFloat(value).toFixed(this.decimalPrecision);
43789     },
43790     
43791     decimalPrecisionFcn : function(v)
43792     {
43793         return Math.floor(v);
43794     },
43795     
43796     validateValue : function(value)
43797     {
43798         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43799             return false;
43800         }
43801         
43802         var num = this.parseValue(value);
43803         
43804         if(isNaN(num)){
43805             this.markInvalid(String.format(this.nanText, value));
43806             return false;
43807         }
43808         
43809         if(num < this.minValue){
43810             this.markInvalid(String.format(this.minText, this.minValue));
43811             return false;
43812         }
43813         
43814         if(num > this.maxValue){
43815             this.markInvalid(String.format(this.maxText, this.maxValue));
43816             return false;
43817         }
43818         
43819         return true;
43820     },
43821     
43822     validate : function()
43823     {
43824         if(this.disabled || this.allowBlank){
43825             this.markValid();
43826             return true;
43827         }
43828         
43829         var currency = this.getCurrency();
43830         
43831         if(this.validateValue(this.getRawValue()) && currency.length){
43832             this.markValid();
43833             return true;
43834         }
43835         
43836         this.markInvalid();
43837         return false;
43838     },
43839     
43840     getName: function()
43841     {
43842         return this.name;
43843     },
43844     
43845     beforeBlur : function()
43846     {
43847         if(!this.castInt){
43848             return;
43849         }
43850         
43851         var v = this.parseValue(this.getRawValue());
43852         
43853         if(v || v == 0){
43854             this.setValue(v);
43855         }
43856     },
43857     
43858     onBlur : function()
43859     {
43860         this.beforeBlur();
43861         
43862         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43863             //this.el.removeClass(this.focusClass);
43864         }
43865         
43866         this.hasFocus = false;
43867         
43868         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43869             this.validate();
43870         }
43871         
43872         var v = this.getValue();
43873         
43874         if(String(v) !== String(this.startValue)){
43875             this.fireEvent('change', this, v, this.startValue);
43876         }
43877         
43878         this.fireEvent("blur", this);
43879     },
43880     
43881     inputEl : function()
43882     {
43883         return this.el.select('.roo-money-amount-input', true).first();
43884     },
43885     
43886     currencyEl : function()
43887     {
43888         return this.el.select('.roo-money-currency-input', true).first();
43889     },
43890     
43891     hiddenEl : function()
43892     {
43893         return this.el.select('input.hidden-number-input',true).first();
43894     }
43895     
43896 });/**
43897  * @class Roo.bootstrap.BezierSignature
43898  * @extends Roo.bootstrap.Component
43899  * Bootstrap BezierSignature class
43900  * This script refer to:
43901  *    Title: Signature Pad
43902  *    Author: szimek
43903  *    Availability: https://github.com/szimek/signature_pad
43904  *
43905  * @constructor
43906  * Create a new BezierSignature
43907  * @param {Object} config The config object
43908  */
43909
43910 Roo.bootstrap.BezierSignature = function(config){
43911     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43912     this.addEvents({
43913         "resize" : true
43914     });
43915 };
43916
43917 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43918 {
43919      
43920     curve_data: [],
43921     
43922     is_empty: true,
43923     
43924     mouse_btn_down: true,
43925     
43926     /**
43927      * @cfg {int} canvas height
43928      */
43929     canvas_height: '200px',
43930     
43931     /**
43932      * @cfg {float|function} Radius of a single dot.
43933      */ 
43934     dot_size: false,
43935     
43936     /**
43937      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43938      */
43939     min_width: 0.5,
43940     
43941     /**
43942      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43943      */
43944     max_width: 2.5,
43945     
43946     /**
43947      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43948      */
43949     throttle: 16,
43950     
43951     /**
43952      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43953      */
43954     min_distance: 5,
43955     
43956     /**
43957      * @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.
43958      */
43959     bg_color: 'rgba(0, 0, 0, 0)',
43960     
43961     /**
43962      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43963      */
43964     dot_color: 'black',
43965     
43966     /**
43967      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43968      */ 
43969     velocity_filter_weight: 0.7,
43970     
43971     /**
43972      * @cfg {function} Callback when stroke begin. 
43973      */
43974     onBegin: false,
43975     
43976     /**
43977      * @cfg {function} Callback when stroke end.
43978      */
43979     onEnd: false,
43980     
43981     getAutoCreate : function()
43982     {
43983         var cls = 'roo-signature column';
43984         
43985         if(this.cls){
43986             cls += ' ' + this.cls;
43987         }
43988         
43989         var col_sizes = [
43990             'lg',
43991             'md',
43992             'sm',
43993             'xs'
43994         ];
43995         
43996         for(var i = 0; i < col_sizes.length; i++) {
43997             if(this[col_sizes[i]]) {
43998                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43999             }
44000         }
44001         
44002         var cfg = {
44003             tag: 'div',
44004             cls: cls,
44005             cn: [
44006                 {
44007                     tag: 'div',
44008                     cls: 'roo-signature-body',
44009                     cn: [
44010                         {
44011                             tag: 'canvas',
44012                             cls: 'roo-signature-body-canvas',
44013                             height: this.canvas_height,
44014                             width: this.canvas_width
44015                         }
44016                     ]
44017                 },
44018                 {
44019                     tag: 'input',
44020                     type: 'file',
44021                     style: 'display: none'
44022                 }
44023             ]
44024         };
44025         
44026         return cfg;
44027     },
44028     
44029     initEvents: function() 
44030     {
44031         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44032         
44033         var canvas = this.canvasEl();
44034         
44035         // mouse && touch event swapping...
44036         canvas.dom.style.touchAction = 'none';
44037         canvas.dom.style.msTouchAction = 'none';
44038         
44039         this.mouse_btn_down = false;
44040         canvas.on('mousedown', this._handleMouseDown, this);
44041         canvas.on('mousemove', this._handleMouseMove, this);
44042         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44043         
44044         if (window.PointerEvent) {
44045             canvas.on('pointerdown', this._handleMouseDown, this);
44046             canvas.on('pointermove', this._handleMouseMove, this);
44047             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44048         }
44049         
44050         if ('ontouchstart' in window) {
44051             canvas.on('touchstart', this._handleTouchStart, this);
44052             canvas.on('touchmove', this._handleTouchMove, this);
44053             canvas.on('touchend', this._handleTouchEnd, this);
44054         }
44055         
44056         Roo.EventManager.onWindowResize(this.resize, this, true);
44057         
44058         // file input event
44059         this.fileEl().on('change', this.uploadImage, this);
44060         
44061         this.clear();
44062         
44063         this.resize();
44064     },
44065     
44066     resize: function(){
44067         
44068         var canvas = this.canvasEl().dom;
44069         var ctx = this.canvasElCtx();
44070         var img_data = false;
44071         
44072         if(canvas.width > 0) {
44073             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44074         }
44075         // setting canvas width will clean img data
44076         canvas.width = 0;
44077         
44078         var style = window.getComputedStyle ? 
44079             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44080             
44081         var padding_left = parseInt(style.paddingLeft) || 0;
44082         var padding_right = parseInt(style.paddingRight) || 0;
44083         
44084         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44085         
44086         if(img_data) {
44087             ctx.putImageData(img_data, 0, 0);
44088         }
44089     },
44090     
44091     _handleMouseDown: function(e)
44092     {
44093         if (e.browserEvent.which === 1) {
44094             this.mouse_btn_down = true;
44095             this.strokeBegin(e);
44096         }
44097     },
44098     
44099     _handleMouseMove: function (e)
44100     {
44101         if (this.mouse_btn_down) {
44102             this.strokeMoveUpdate(e);
44103         }
44104     },
44105     
44106     _handleMouseUp: function (e)
44107     {
44108         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44109             this.mouse_btn_down = false;
44110             this.strokeEnd(e);
44111         }
44112     },
44113     
44114     _handleTouchStart: function (e) {
44115         
44116         e.preventDefault();
44117         if (e.browserEvent.targetTouches.length === 1) {
44118             // var touch = e.browserEvent.changedTouches[0];
44119             // this.strokeBegin(touch);
44120             
44121              this.strokeBegin(e); // assume e catching the correct xy...
44122         }
44123     },
44124     
44125     _handleTouchMove: function (e) {
44126         e.preventDefault();
44127         // var touch = event.targetTouches[0];
44128         // _this._strokeMoveUpdate(touch);
44129         this.strokeMoveUpdate(e);
44130     },
44131     
44132     _handleTouchEnd: function (e) {
44133         var wasCanvasTouched = e.target === this.canvasEl().dom;
44134         if (wasCanvasTouched) {
44135             e.preventDefault();
44136             // var touch = event.changedTouches[0];
44137             // _this._strokeEnd(touch);
44138             this.strokeEnd(e);
44139         }
44140     },
44141     
44142     reset: function () {
44143         this._lastPoints = [];
44144         this._lastVelocity = 0;
44145         this._lastWidth = (this.min_width + this.max_width) / 2;
44146         this.canvasElCtx().fillStyle = this.dot_color;
44147     },
44148     
44149     strokeMoveUpdate: function(e)
44150     {
44151         this.strokeUpdate(e);
44152         
44153         if (this.throttle) {
44154             this.throttleStroke(this.strokeUpdate, this.throttle);
44155         }
44156         else {
44157             this.strokeUpdate(e);
44158         }
44159     },
44160     
44161     strokeBegin: function(e)
44162     {
44163         var newPointGroup = {
44164             color: this.dot_color,
44165             points: []
44166         };
44167         
44168         if (typeof this.onBegin === 'function') {
44169             this.onBegin(e);
44170         }
44171         
44172         this.curve_data.push(newPointGroup);
44173         this.reset();
44174         this.strokeUpdate(e);
44175     },
44176     
44177     strokeUpdate: function(e)
44178     {
44179         var rect = this.canvasEl().dom.getBoundingClientRect();
44180         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44181         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44182         var lastPoints = lastPointGroup.points;
44183         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44184         var isLastPointTooClose = lastPoint
44185             ? point.distanceTo(lastPoint) <= this.min_distance
44186             : false;
44187         var color = lastPointGroup.color;
44188         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44189             var curve = this.addPoint(point);
44190             if (!lastPoint) {
44191                 this.drawDot({color: color, point: point});
44192             }
44193             else if (curve) {
44194                 this.drawCurve({color: color, curve: curve});
44195             }
44196             lastPoints.push({
44197                 time: point.time,
44198                 x: point.x,
44199                 y: point.y
44200             });
44201         }
44202     },
44203     
44204     strokeEnd: function(e)
44205     {
44206         this.strokeUpdate(e);
44207         if (typeof this.onEnd === 'function') {
44208             this.onEnd(e);
44209         }
44210     },
44211     
44212     addPoint:  function (point) {
44213         var _lastPoints = this._lastPoints;
44214         _lastPoints.push(point);
44215         if (_lastPoints.length > 2) {
44216             if (_lastPoints.length === 3) {
44217                 _lastPoints.unshift(_lastPoints[0]);
44218             }
44219             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44220             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44221             _lastPoints.shift();
44222             return curve;
44223         }
44224         return null;
44225     },
44226     
44227     calculateCurveWidths: function (startPoint, endPoint) {
44228         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44229             (1 - this.velocity_filter_weight) * this._lastVelocity;
44230
44231         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44232         var widths = {
44233             end: newWidth,
44234             start: this._lastWidth
44235         };
44236         
44237         this._lastVelocity = velocity;
44238         this._lastWidth = newWidth;
44239         return widths;
44240     },
44241     
44242     drawDot: function (_a) {
44243         var color = _a.color, point = _a.point;
44244         var ctx = this.canvasElCtx();
44245         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44246         ctx.beginPath();
44247         this.drawCurveSegment(point.x, point.y, width);
44248         ctx.closePath();
44249         ctx.fillStyle = color;
44250         ctx.fill();
44251     },
44252     
44253     drawCurve: function (_a) {
44254         var color = _a.color, curve = _a.curve;
44255         var ctx = this.canvasElCtx();
44256         var widthDelta = curve.endWidth - curve.startWidth;
44257         var drawSteps = Math.floor(curve.length()) * 2;
44258         ctx.beginPath();
44259         ctx.fillStyle = color;
44260         for (var i = 0; i < drawSteps; i += 1) {
44261         var t = i / drawSteps;
44262         var tt = t * t;
44263         var ttt = tt * t;
44264         var u = 1 - t;
44265         var uu = u * u;
44266         var uuu = uu * u;
44267         var x = uuu * curve.startPoint.x;
44268         x += 3 * uu * t * curve.control1.x;
44269         x += 3 * u * tt * curve.control2.x;
44270         x += ttt * curve.endPoint.x;
44271         var y = uuu * curve.startPoint.y;
44272         y += 3 * uu * t * curve.control1.y;
44273         y += 3 * u * tt * curve.control2.y;
44274         y += ttt * curve.endPoint.y;
44275         var width = curve.startWidth + ttt * widthDelta;
44276         this.drawCurveSegment(x, y, width);
44277         }
44278         ctx.closePath();
44279         ctx.fill();
44280     },
44281     
44282     drawCurveSegment: function (x, y, width) {
44283         var ctx = this.canvasElCtx();
44284         ctx.moveTo(x, y);
44285         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44286         this.is_empty = false;
44287     },
44288     
44289     clear: function()
44290     {
44291         var ctx = this.canvasElCtx();
44292         var canvas = this.canvasEl().dom;
44293         ctx.fillStyle = this.bg_color;
44294         ctx.clearRect(0, 0, canvas.width, canvas.height);
44295         ctx.fillRect(0, 0, canvas.width, canvas.height);
44296         this.curve_data = [];
44297         this.reset();
44298         this.is_empty = true;
44299     },
44300     
44301     fileEl: function()
44302     {
44303         return  this.el.select('input',true).first();
44304     },
44305     
44306     canvasEl: function()
44307     {
44308         return this.el.select('canvas',true).first();
44309     },
44310     
44311     canvasElCtx: function()
44312     {
44313         return this.el.select('canvas',true).first().dom.getContext('2d');
44314     },
44315     
44316     getImage: function(type)
44317     {
44318         if(this.is_empty) {
44319             return false;
44320         }
44321         
44322         // encryption ?
44323         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44324     },
44325     
44326     drawFromImage: function(img_src)
44327     {
44328         var img = new Image();
44329         
44330         img.onload = function(){
44331             this.canvasElCtx().drawImage(img, 0, 0);
44332         }.bind(this);
44333         
44334         img.src = img_src;
44335         
44336         this.is_empty = false;
44337     },
44338     
44339     selectImage: function()
44340     {
44341         this.fileEl().dom.click();
44342     },
44343     
44344     uploadImage: function(e)
44345     {
44346         var reader = new FileReader();
44347         
44348         reader.onload = function(e){
44349             var img = new Image();
44350             img.onload = function(){
44351                 this.reset();
44352                 this.canvasElCtx().drawImage(img, 0, 0);
44353             }.bind(this);
44354             img.src = e.target.result;
44355         }.bind(this);
44356         
44357         reader.readAsDataURL(e.target.files[0]);
44358     },
44359     
44360     // Bezier Point Constructor
44361     Point: (function () {
44362         function Point(x, y, time) {
44363             this.x = x;
44364             this.y = y;
44365             this.time = time || Date.now();
44366         }
44367         Point.prototype.distanceTo = function (start) {
44368             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44369         };
44370         Point.prototype.equals = function (other) {
44371             return this.x === other.x && this.y === other.y && this.time === other.time;
44372         };
44373         Point.prototype.velocityFrom = function (start) {
44374             return this.time !== start.time
44375             ? this.distanceTo(start) / (this.time - start.time)
44376             : 0;
44377         };
44378         return Point;
44379     }()),
44380     
44381     
44382     // Bezier Constructor
44383     Bezier: (function () {
44384         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44385             this.startPoint = startPoint;
44386             this.control2 = control2;
44387             this.control1 = control1;
44388             this.endPoint = endPoint;
44389             this.startWidth = startWidth;
44390             this.endWidth = endWidth;
44391         }
44392         Bezier.fromPoints = function (points, widths, scope) {
44393             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44394             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44395             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44396         };
44397         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44398             var dx1 = s1.x - s2.x;
44399             var dy1 = s1.y - s2.y;
44400             var dx2 = s2.x - s3.x;
44401             var dy2 = s2.y - s3.y;
44402             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44403             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44404             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44405             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44406             var dxm = m1.x - m2.x;
44407             var dym = m1.y - m2.y;
44408             var k = l2 / (l1 + l2);
44409             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44410             var tx = s2.x - cm.x;
44411             var ty = s2.y - cm.y;
44412             return {
44413                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44414                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44415             };
44416         };
44417         Bezier.prototype.length = function () {
44418             var steps = 10;
44419             var length = 0;
44420             var px;
44421             var py;
44422             for (var i = 0; i <= steps; i += 1) {
44423                 var t = i / steps;
44424                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44425                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44426                 if (i > 0) {
44427                     var xdiff = cx - px;
44428                     var ydiff = cy - py;
44429                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44430                 }
44431                 px = cx;
44432                 py = cy;
44433             }
44434             return length;
44435         };
44436         Bezier.prototype.point = function (t, start, c1, c2, end) {
44437             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44438             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44439             + (3.0 * c2 * (1.0 - t) * t * t)
44440             + (end * t * t * t);
44441         };
44442         return Bezier;
44443     }()),
44444     
44445     throttleStroke: function(fn, wait) {
44446       if (wait === void 0) { wait = 250; }
44447       var previous = 0;
44448       var timeout = null;
44449       var result;
44450       var storedContext;
44451       var storedArgs;
44452       var later = function () {
44453           previous = Date.now();
44454           timeout = null;
44455           result = fn.apply(storedContext, storedArgs);
44456           if (!timeout) {
44457               storedContext = null;
44458               storedArgs = [];
44459           }
44460       };
44461       return function wrapper() {
44462           var args = [];
44463           for (var _i = 0; _i < arguments.length; _i++) {
44464               args[_i] = arguments[_i];
44465           }
44466           var now = Date.now();
44467           var remaining = wait - (now - previous);
44468           storedContext = this;
44469           storedArgs = args;
44470           if (remaining <= 0 || remaining > wait) {
44471               if (timeout) {
44472                   clearTimeout(timeout);
44473                   timeout = null;
44474               }
44475               previous = now;
44476               result = fn.apply(storedContext, storedArgs);
44477               if (!timeout) {
44478                   storedContext = null;
44479                   storedArgs = [];
44480               }
44481           }
44482           else if (!timeout) {
44483               timeout = window.setTimeout(later, remaining);
44484           }
44485           return result;
44486       };
44487   }
44488   
44489 });
44490
44491  
44492
44493