Partial Fix #6524 - test client access
[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         this.el.dom.removeAttribute("disabled");
1358     },
1359     
1360     /**
1361      * Disable this button
1362      */
1363     disable : function()
1364     {
1365         this.disabled = true;
1366         this.el.addClass('disabled');
1367         this.el.attr("disabled", "disabled")
1368     },
1369      /**
1370      * sets the active state on/off, 
1371      * @param {Boolean} state (optional) Force a particular state
1372      */
1373     setActive : function(v) {
1374         
1375         this.el[v ? 'addClass' : 'removeClass']('active');
1376         this.pressed = v;
1377     },
1378      /**
1379      * toggles the current active state 
1380      */
1381     toggleActive : function(e)
1382     {
1383         this.setActive(!this.pressed); // this modifies pressed...
1384         this.fireEvent('toggle', this, e, this.pressed);
1385     },
1386      /**
1387      * get the current active state
1388      * @return {boolean} true if it's active
1389      */
1390     isActive : function()
1391     {
1392         return this.el.hasClass('active');
1393     },
1394     /**
1395      * set the text of the first selected button
1396      */
1397     setText : function(str)
1398     {
1399         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1400     },
1401     /**
1402      * get the text of the first selected button
1403      */
1404     getText : function()
1405     {
1406         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1407     },
1408     
1409     setWeight : function(str)
1410     {
1411         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1412         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1413         this.weight = str;
1414         var outline = this.outline ? 'outline-' : '';
1415         if (str == 'default') {
1416             this.el.addClass('btn-default btn-outline-secondary');        
1417             return;
1418         }
1419         this.el.addClass('btn-' + outline + str);        
1420     }
1421     
1422     
1423 });
1424 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1425
1426 Roo.bootstrap.Button.weights = [
1427     'default',
1428     'secondary' ,
1429     'primary',
1430     'success',
1431     'info',
1432     'warning',
1433     'danger',
1434     'link',
1435     'light',
1436     'dark'              
1437    
1438 ];/*
1439  * - LGPL
1440  *
1441  * column
1442  * 
1443  */
1444
1445 /**
1446  * @class Roo.bootstrap.Column
1447  * @extends Roo.bootstrap.Component
1448  * Bootstrap Column class
1449  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1450  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1451  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1452  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1453  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1454  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1455  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1456  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1457  *
1458  * 
1459  * @cfg {Boolean} hidden (true|false) hide the element
1460  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1461  * @cfg {String} fa (ban|check|...) font awesome icon
1462  * @cfg {Number} fasize (1|2|....) font awsome size
1463
1464  * @cfg {String} icon (info-sign|check|...) glyphicon name
1465
1466  * @cfg {String} html content of column.
1467  * 
1468  * @constructor
1469  * Create a new Column
1470  * @param {Object} config The config object
1471  */
1472
1473 Roo.bootstrap.Column = function(config){
1474     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1475 };
1476
1477 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1478     
1479     xs: false,
1480     sm: false,
1481     md: false,
1482     lg: false,
1483     xsoff: false,
1484     smoff: false,
1485     mdoff: false,
1486     lgoff: false,
1487     html: '',
1488     offset: 0,
1489     alert: false,
1490     fa: false,
1491     icon : false,
1492     hidden : false,
1493     fasize : 1,
1494     
1495     getAutoCreate : function(){
1496         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1497         
1498         cfg = {
1499             tag: 'div',
1500             cls: 'column'
1501         };
1502         
1503         var settings=this;
1504         var sizes =   ['xs','sm','md','lg'];
1505         sizes.map(function(size ,ix){
1506             //Roo.log( size + ':' + settings[size]);
1507             
1508             if (settings[size+'off'] !== false) {
1509                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1510             }
1511             
1512             if (settings[size] === false) {
1513                 return;
1514             }
1515             
1516             if (!settings[size]) { // 0 = hidden
1517                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1518                 // bootsrap4
1519                 for (var i = ix; i > -1; i--) {
1520                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1521                 }
1522                 
1523                 
1524                 return;
1525             }
1526             cfg.cls += ' col-' + size + '-' + settings[size] + (
1527                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1528             );
1529             
1530         });
1531         
1532         if (this.hidden) {
1533             cfg.cls += ' hidden';
1534         }
1535         
1536         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1537             cfg.cls +=' alert alert-' + this.alert;
1538         }
1539         
1540         
1541         if (this.html.length) {
1542             cfg.html = this.html;
1543         }
1544         if (this.fa) {
1545             var fasize = '';
1546             if (this.fasize > 1) {
1547                 fasize = ' fa-' + this.fasize + 'x';
1548             }
1549             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1550             
1551             
1552         }
1553         if (this.icon) {
1554             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1555         }
1556         
1557         return cfg;
1558     }
1559    
1560 });
1561
1562  
1563
1564  /*
1565  * - LGPL
1566  *
1567  * page container.
1568  * 
1569  */
1570
1571
1572 /**
1573  * @class Roo.bootstrap.Container
1574  * @extends Roo.bootstrap.Component
1575  * Bootstrap Container class
1576  * @cfg {Boolean} jumbotron is it a jumbotron element
1577  * @cfg {String} html content of element
1578  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1579  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1580  * @cfg {String} header content of header (for panel)
1581  * @cfg {String} footer content of footer (for panel)
1582  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1583  * @cfg {String} tag (header|aside|section) type of HTML tag.
1584  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1585  * @cfg {String} fa font awesome icon
1586  * @cfg {String} icon (info-sign|check|...) glyphicon name
1587  * @cfg {Boolean} hidden (true|false) hide the element
1588  * @cfg {Boolean} expandable (true|false) default false
1589  * @cfg {Boolean} expanded (true|false) default true
1590  * @cfg {String} rheader contet on the right of header
1591  * @cfg {Boolean} clickable (true|false) default false
1592
1593  *     
1594  * @constructor
1595  * Create a new Container
1596  * @param {Object} config The config object
1597  */
1598
1599 Roo.bootstrap.Container = function(config){
1600     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1601     
1602     this.addEvents({
1603         // raw events
1604          /**
1605          * @event expand
1606          * After the panel has been expand
1607          * 
1608          * @param {Roo.bootstrap.Container} this
1609          */
1610         "expand" : true,
1611         /**
1612          * @event collapse
1613          * After the panel has been collapsed
1614          * 
1615          * @param {Roo.bootstrap.Container} this
1616          */
1617         "collapse" : true,
1618         /**
1619          * @event click
1620          * When a element is chick
1621          * @param {Roo.bootstrap.Container} this
1622          * @param {Roo.EventObject} e
1623          */
1624         "click" : true
1625     });
1626 };
1627
1628 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1629     
1630     jumbotron : false,
1631     well: '',
1632     panel : '',
1633     header: '',
1634     footer : '',
1635     sticky: '',
1636     tag : false,
1637     alert : false,
1638     fa: false,
1639     icon : false,
1640     expandable : false,
1641     rheader : '',
1642     expanded : true,
1643     clickable: false,
1644   
1645      
1646     getChildContainer : function() {
1647         
1648         if(!this.el){
1649             return false;
1650         }
1651         
1652         if (this.panel.length) {
1653             return this.el.select('.panel-body',true).first();
1654         }
1655         
1656         return this.el;
1657     },
1658     
1659     
1660     getAutoCreate : function(){
1661         
1662         var cfg = {
1663             tag : this.tag || 'div',
1664             html : '',
1665             cls : ''
1666         };
1667         if (this.jumbotron) {
1668             cfg.cls = 'jumbotron';
1669         }
1670         
1671         
1672         
1673         // - this is applied by the parent..
1674         //if (this.cls) {
1675         //    cfg.cls = this.cls + '';
1676         //}
1677         
1678         if (this.sticky.length) {
1679             
1680             var bd = Roo.get(document.body);
1681             if (!bd.hasClass('bootstrap-sticky')) {
1682                 bd.addClass('bootstrap-sticky');
1683                 Roo.select('html',true).setStyle('height', '100%');
1684             }
1685              
1686             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1687         }
1688         
1689         
1690         if (this.well.length) {
1691             switch (this.well) {
1692                 case 'lg':
1693                 case 'sm':
1694                     cfg.cls +=' well well-' +this.well;
1695                     break;
1696                 default:
1697                     cfg.cls +=' well';
1698                     break;
1699             }
1700         }
1701         
1702         if (this.hidden) {
1703             cfg.cls += ' hidden';
1704         }
1705         
1706         
1707         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1708             cfg.cls +=' alert alert-' + this.alert;
1709         }
1710         
1711         var body = cfg;
1712         
1713         if (this.panel.length) {
1714             cfg.cls += ' panel panel-' + this.panel;
1715             cfg.cn = [];
1716             if (this.header.length) {
1717                 
1718                 var h = [];
1719                 
1720                 if(this.expandable){
1721                     
1722                     cfg.cls = cfg.cls + ' expandable';
1723                     
1724                     h.push({
1725                         tag: 'i',
1726                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1727                     });
1728                     
1729                 }
1730                 
1731                 h.push(
1732                     {
1733                         tag: 'span',
1734                         cls : 'panel-title',
1735                         html : (this.expandable ? '&nbsp;' : '') + this.header
1736                     },
1737                     {
1738                         tag: 'span',
1739                         cls: 'panel-header-right',
1740                         html: this.rheader
1741                     }
1742                 );
1743                 
1744                 cfg.cn.push({
1745                     cls : 'panel-heading',
1746                     style : this.expandable ? 'cursor: pointer' : '',
1747                     cn : h
1748                 });
1749                 
1750             }
1751             
1752             body = false;
1753             cfg.cn.push({
1754                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1755                 html : this.html
1756             });
1757             
1758             
1759             if (this.footer.length) {
1760                 cfg.cn.push({
1761                     cls : 'panel-footer',
1762                     html : this.footer
1763                     
1764                 });
1765             }
1766             
1767         }
1768         
1769         if (body) {
1770             body.html = this.html || cfg.html;
1771             // prefix with the icons..
1772             if (this.fa) {
1773                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1774             }
1775             if (this.icon) {
1776                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1777             }
1778             
1779             
1780         }
1781         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1782             cfg.cls =  'container';
1783         }
1784         
1785         return cfg;
1786     },
1787     
1788     initEvents: function() 
1789     {
1790         if(this.expandable){
1791             var headerEl = this.headerEl();
1792         
1793             if(headerEl){
1794                 headerEl.on('click', this.onToggleClick, this);
1795             }
1796         }
1797         
1798         if(this.clickable){
1799             this.el.on('click', this.onClick, this);
1800         }
1801         
1802     },
1803     
1804     onToggleClick : function()
1805     {
1806         var headerEl = this.headerEl();
1807         
1808         if(!headerEl){
1809             return;
1810         }
1811         
1812         if(this.expanded){
1813             this.collapse();
1814             return;
1815         }
1816         
1817         this.expand();
1818     },
1819     
1820     expand : function()
1821     {
1822         if(this.fireEvent('expand', this)) {
1823             
1824             this.expanded = true;
1825             
1826             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1827             
1828             this.el.select('.panel-body',true).first().removeClass('hide');
1829             
1830             var toggleEl = this.toggleEl();
1831
1832             if(!toggleEl){
1833                 return;
1834             }
1835
1836             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1837         }
1838         
1839     },
1840     
1841     collapse : function()
1842     {
1843         if(this.fireEvent('collapse', this)) {
1844             
1845             this.expanded = false;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1848             this.el.select('.panel-body',true).first().addClass('hide');
1849         
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1857         }
1858     },
1859     
1860     toggleEl : function()
1861     {
1862         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1863             return;
1864         }
1865         
1866         return this.el.select('.panel-heading .fa',true).first();
1867     },
1868     
1869     headerEl : function()
1870     {
1871         if(!this.el || !this.panel.length || !this.header.length){
1872             return;
1873         }
1874         
1875         return this.el.select('.panel-heading',true).first()
1876     },
1877     
1878     bodyEl : function()
1879     {
1880         if(!this.el || !this.panel.length){
1881             return;
1882         }
1883         
1884         return this.el.select('.panel-body',true).first()
1885     },
1886     
1887     titleEl : function()
1888     {
1889         if(!this.el || !this.panel.length || !this.header.length){
1890             return;
1891         }
1892         
1893         return this.el.select('.panel-title',true).first();
1894     },
1895     
1896     setTitle : function(v)
1897     {
1898         var titleEl = this.titleEl();
1899         
1900         if(!titleEl){
1901             return;
1902         }
1903         
1904         titleEl.dom.innerHTML = v;
1905     },
1906     
1907     getTitle : function()
1908     {
1909         
1910         var titleEl = this.titleEl();
1911         
1912         if(!titleEl){
1913             return '';
1914         }
1915         
1916         return titleEl.dom.innerHTML;
1917     },
1918     
1919     setRightTitle : function(v)
1920     {
1921         var t = this.el.select('.panel-header-right',true).first();
1922         
1923         if(!t){
1924             return;
1925         }
1926         
1927         t.dom.innerHTML = v;
1928     },
1929     
1930     onClick : function(e)
1931     {
1932         e.preventDefault();
1933         
1934         this.fireEvent('click', this, e);
1935     }
1936 });
1937
1938  /*
1939  *  - LGPL
1940  *
1941  *  This is BS4's Card element.. - similar to our containers probably..
1942  * 
1943  */
1944 /**
1945  * @class Roo.bootstrap.Card
1946  * @extends Roo.bootstrap.Component
1947  * Bootstrap Card class
1948  *
1949  *
1950  * possible... may not be implemented..
1951  * @cfg {String} header_image  src url of image.
1952  * @cfg {String|Object} header
1953  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1954  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1955  * 
1956  * @cfg {String} title
1957  * @cfg {String} subtitle
1958  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1959  * @cfg {String} footer
1960  
1961  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1962  * 
1963  * @cfg {String} margin (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1968  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1969  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1970  *
1971  * @cfg {String} padding (0|1|2|3|4|5)
1972  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1973  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1974  * @cfg {String} padding_left (0|1|2|3|4|5)
1975  * @cfg {String} padding_right (0|1|2|3|4|5)
1976  * @cfg {String} padding_x (0|1|2|3|4|5)
1977  * @cfg {String} padding_y (0|1|2|3|4|5)
1978  *
1979  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1983  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1984  
1985  * @config {Boolean} dragable  if this card can be dragged.
1986  * @config {String} drag_group  group for drag
1987  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1988  * @config {String} drop_group  group for drag
1989  * 
1990  * @config {Boolean} collapsable can the body be collapsed.
1991  * @config {Boolean} collapsed is the body collapsed when rendered...
1992  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1993  * @config {Boolean} rotated is the body rotated when rendered...
1994  * 
1995  * @constructor
1996  * Create a new Container
1997  * @param {Object} config The config object
1998  */
1999
2000 Roo.bootstrap.Card = function(config){
2001     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2002     
2003     this.addEvents({
2004          // raw events
2005         /**
2006          * @event drop
2007          * When a element a card is dropped
2008          * @param {Roo.bootstrap.Card} this
2009          *
2010          * 
2011          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2012          * @param {String} position 'above' or 'below'
2013          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2014         
2015          */
2016         'drop' : true,
2017          /**
2018          * @event rotate
2019          * When a element a card is rotate
2020          * @param {Roo.bootstrap.Card} this
2021          * @param {Roo.Element} n the node being dropped?
2022          * @param {Boolean} rotate status
2023          */
2024         'rotate' : true,
2025         /**
2026          * @event cardover
2027          * When a card element is dragged over ready to drop (return false to block dropable)
2028          * @param {Roo.bootstrap.Card} this
2029          * @param {Object} data from dragdrop 
2030          */
2031          'cardover' : true 
2032         
2033     });
2034 };
2035
2036
2037 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2038     
2039     
2040     weight : '',
2041     
2042     margin: '', /// may be better in component?
2043     margin_top: '', 
2044     margin_bottom: '', 
2045     margin_left: '',
2046     margin_right: '',
2047     margin_x: '',
2048     margin_y: '',
2049     
2050     padding : '',
2051     padding_top: '', 
2052     padding_bottom: '', 
2053     padding_left: '',
2054     padding_right: '',
2055     padding_x: '',
2056     padding_y: '',
2057     
2058     display: '', 
2059     display_xs: '', 
2060     display_sm: '', 
2061     display_lg: '',
2062     display_xl: '',
2063  
2064     header_image  : '',
2065     header : '',
2066     header_size : 0,
2067     title : '',
2068     subtitle : '',
2069     html : '',
2070     footer: '',
2071
2072     collapsable : false,
2073     collapsed : false,
2074     rotateable : false,
2075     rotated : false,
2076     
2077     dragable : false,
2078     drag_group : false,
2079     dropable : false,
2080     drop_group : false,
2081     childContainer : false,
2082     dropEl : false, /// the dom placeholde element that indicates drop location.
2083     containerEl: false, // body container
2084     bodyEl: false, // card-body
2085     headerContainerEl : false, //
2086     headerEl : false,
2087     header_imageEl : false,
2088     
2089     layoutCls : function()
2090     {
2091         var cls = '';
2092         var t = this;
2093         Roo.log(this.margin_bottom.length);
2094         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2095             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2096             
2097             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2098                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2099             }
2100             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2101                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2102             }
2103         });
2104         
2105         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2106             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2107                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2108             }
2109         });
2110         
2111         // more generic support?
2112         if (this.hidden) {
2113             cls += ' d-none';
2114         }
2115         
2116         return cls;
2117     },
2118  
2119        // Roo.log("Call onRender: " + this.xtype);
2120         /*  We are looking at something like this.
2121 <div class="card">
2122     <img src="..." class="card-img-top" alt="...">
2123     <div class="card-body">
2124         <h5 class="card-title">Card title</h5>
2125          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2126
2127         >> this bit is really the body...
2128         <div> << we will ad dthis in hopefully it will not break shit.
2129         
2130         ** card text does not actually have any styling...
2131         
2132             <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>
2133         
2134         </div> <<
2135           <a href="#" class="card-link">Card link</a>
2136           
2137     </div>
2138     <div class="card-footer">
2139         <small class="text-muted">Last updated 3 mins ago</small>
2140     </div>
2141 </div>
2142          */
2143     getAutoCreate : function(){
2144         
2145         var cfg = {
2146             tag : 'div',
2147             cls : 'card',
2148             cn : [ ]
2149         };
2150         
2151         if (this.weight.length && this.weight != 'light') {
2152             cfg.cls += ' text-white';
2153         } else {
2154             cfg.cls += ' text-dark'; // need as it's nested..
2155         }
2156         if (this.weight.length) {
2157             cfg.cls += ' bg-' + this.weight;
2158         }
2159         
2160         cfg.cls += ' ' + this.layoutCls(); 
2161         
2162         var hdr = false;
2163         var hdr_ctr = false;
2164         if (this.header.length) {
2165             hdr = {
2166                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2167                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2168                 cn : []
2169             };
2170             cfg.cn.push(hdr);
2171             hdr_ctr = hdr;
2172         } else {
2173             hdr = {
2174                 tag : 'div',
2175                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2176                 cn : []
2177             };
2178             cfg.cn.push(hdr);
2179             hdr_ctr = hdr;
2180         }
2181         if (this.collapsable) {
2182             hdr_ctr = {
2183             tag : 'a',
2184             cls : 'd-block user-select-none',
2185             cn: [
2186                     {
2187                         tag: 'i',
2188                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2189                     }
2190                    
2191                 ]
2192             };
2193             hdr.cn.push(hdr_ctr);
2194         }
2195         
2196         hdr_ctr.cn.push(        {
2197             tag: 'span',
2198             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2199             html : this.header
2200         });
2201         
2202         
2203         if (this.header_image.length) {
2204             cfg.cn.push({
2205                 tag : 'img',
2206                 cls : 'card-img-top',
2207                 src: this.header_image // escape?
2208             });
2209         } else {
2210             cfg.cn.push({
2211                     tag : 'div',
2212                     cls : 'card-img-top d-none' 
2213                 });
2214         }
2215             
2216         var body = {
2217             tag : 'div',
2218             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2219             cn : []
2220         };
2221         var obody = body;
2222         if (this.collapsable || this.rotateable) {
2223             obody = {
2224                 tag: 'div',
2225                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2226                 cn : [  body ]
2227             };
2228         }
2229         
2230         cfg.cn.push(obody);
2231         
2232         if (this.title.length) {
2233             body.cn.push({
2234                 tag : 'div',
2235                 cls : 'card-title',
2236                 src: this.title // escape?
2237             });
2238         }  
2239         
2240         if (this.subtitle.length) {
2241             body.cn.push({
2242                 tag : 'div',
2243                 cls : 'card-title',
2244                 src: this.subtitle // escape?
2245             });
2246         }
2247         
2248         body.cn.push({
2249             tag : 'div',
2250             cls : 'roo-card-body-ctr'
2251         });
2252         
2253         if (this.html.length) {
2254             body.cn.push({
2255                 tag: 'div',
2256                 html : this.html
2257             });
2258         }
2259         // fixme ? handle objects?
2260         
2261         if (this.footer.length) {
2262            
2263             cfg.cn.push({
2264                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2265                 html : this.footer
2266             });
2267             
2268         } else {
2269             cfg.cn.push({cls : 'card-footer d-none'});
2270         }
2271         
2272         // footer...
2273         
2274         return cfg;
2275     },
2276     
2277     
2278     getCardHeader : function()
2279     {
2280         var  ret = this.el.select('.card-header',true).first();
2281         if (ret.hasClass('d-none')) {
2282             ret.removeClass('d-none');
2283         }
2284         
2285         return ret;
2286     },
2287     getCardFooter : function()
2288     {
2289         var  ret = this.el.select('.card-footer',true).first();
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293         
2294         return ret;
2295     },
2296     getCardImageTop : function()
2297     {
2298         var  ret = this.header_imageEl;
2299         if (ret.hasClass('d-none')) {
2300             ret.removeClass('d-none');
2301         }
2302             
2303         return ret;
2304     },
2305     
2306     getChildContainer : function()
2307     {
2308         
2309         if(!this.el){
2310             return false;
2311         }
2312         return this.el.select('.roo-card-body-ctr',true).first();    
2313     },
2314     
2315     initEvents: function() 
2316     {
2317         this.bodyEl = this.el.select('.card-body',true).first(); 
2318         this.containerEl = this.getChildContainer();
2319         if(this.dragable){
2320             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2321                     containerScroll: true,
2322                     ddGroup: this.drag_group || 'default_card_drag_group'
2323             });
2324             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2325         }
2326         if (this.dropable) {
2327             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2328                 containerScroll: true,
2329                 ddGroup: this.drop_group || 'default_card_drag_group'
2330             });
2331             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2332             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2333             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2334             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2335             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2336         }
2337         
2338         if (this.collapsable) {
2339             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2340         }
2341         if (this.rotateable) {
2342             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2343         }
2344         this.collapsableEl = this.el.select('.roo-collapsable').first();
2345          
2346         this.footerEl = this.el.select('.card-footer').first();
2347         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2348         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2349         this.headerEl = this.el.select('.card-header',true).first();
2350         
2351         if (this.rotated) {
2352             this.el.addClass('roo-card-rotated');
2353             this.fireEvent('rotate', this, true);
2354         }
2355         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2356         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2357         
2358     },
2359     getDragData : function(e)
2360     {
2361         var target = this.getEl();
2362         if (target) {
2363             //this.handleSelection(e);
2364             
2365             var dragData = {
2366                 source: this,
2367                 copy: false,
2368                 nodes: this.getEl(),
2369                 records: []
2370             };
2371             
2372             
2373             dragData.ddel = target.dom ;    // the div element
2374             Roo.log(target.getWidth( ));
2375             dragData.ddel.style.width = target.getWidth() + 'px';
2376             
2377             return dragData;
2378         }
2379         return false;
2380     },
2381     /**
2382     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2383     *    whole Element becomes the target, and this causes the drop gesture to append.
2384     *
2385     *    Returns an object:
2386     *     {
2387            
2388            position : 'below' or 'above'
2389            card  : relateive to card OBJECT (or true for no cards listed)
2390            items_n : relative to nth item in list
2391            card_n : relative to  nth card in list
2392     }
2393     *
2394     *    
2395     */
2396     getTargetFromEvent : function(e, dragged_card_el)
2397     {
2398         var target = e.getTarget();
2399         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2400             target = target.parentNode;
2401         }
2402         
2403         var ret = {
2404             position: '',
2405             cards : [],
2406             card_n : -1,
2407             items_n : -1,
2408             card : false 
2409         };
2410         
2411         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2412         // see if target is one of the 'cards'...
2413         
2414         
2415         //Roo.log(this.items.length);
2416         var pos = false;
2417         
2418         var last_card_n = 0;
2419         var cards_len  = 0;
2420         for (var i = 0;i< this.items.length;i++) {
2421             
2422             if (!this.items[i].el.hasClass('card')) {
2423                  continue;
2424             }
2425             pos = this.getDropPoint(e, this.items[i].el.dom);
2426             
2427             cards_len = ret.cards.length;
2428             //Roo.log(this.items[i].el.dom.id);
2429             ret.cards.push(this.items[i]);
2430             last_card_n  = i;
2431             if (ret.card_n < 0 && pos == 'above') {
2432                 ret.position = cards_len > 0 ? 'below' : pos;
2433                 ret.items_n = i > 0 ? i - 1 : 0;
2434                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2435                 ret.card = ret.cards[ret.card_n];
2436             }
2437         }
2438         if (!ret.cards.length) {
2439             ret.card = true;
2440             ret.position = 'below';
2441             ret.items_n;
2442             return ret;
2443         }
2444         // could not find a card.. stick it at the end..
2445         if (ret.card_n < 0) {
2446             ret.card_n = last_card_n;
2447             ret.card = ret.cards[last_card_n];
2448             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2449             ret.position = 'below';
2450         }
2451         
2452         if (this.items[ret.items_n].el == dragged_card_el) {
2453             return false;
2454         }
2455         
2456         if (ret.position == 'below') {
2457             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2458             
2459             if (card_after  && card_after.el == dragged_card_el) {
2460                 return false;
2461             }
2462             return ret;
2463         }
2464         
2465         // its's after ..
2466         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2467         
2468         if (card_before  && card_before.el == dragged_card_el) {
2469             return false;
2470         }
2471         
2472         return ret;
2473     },
2474     
2475     onNodeEnter : function(n, dd, e, data){
2476         return false;
2477     },
2478     onNodeOver : function(n, dd, e, data)
2479     {
2480        
2481         var target_info = this.getTargetFromEvent(e,data.source.el);
2482         if (target_info === false) {
2483             this.dropPlaceHolder('hide');
2484             return false;
2485         }
2486         Roo.log(['getTargetFromEvent', target_info ]);
2487         
2488         
2489         if (this.fireEvent('cardover', this, [ data ]) === false) {
2490             return false;
2491         }
2492         
2493         this.dropPlaceHolder('show', target_info,data);
2494         
2495         return false; 
2496     },
2497     onNodeOut : function(n, dd, e, data){
2498         this.dropPlaceHolder('hide');
2499      
2500     },
2501     onNodeDrop : function(n, dd, e, data)
2502     {
2503         
2504         // call drop - return false if
2505         
2506         // this could actually fail - if the Network drops..
2507         // we will ignore this at present..- client should probably reload
2508         // the whole set of cards if stuff like that fails.
2509         
2510         
2511         var info = this.getTargetFromEvent(e,data.source.el);
2512         if (info === false) {
2513             return false;
2514         }
2515         this.dropPlaceHolder('hide');
2516   
2517           
2518     
2519         this.acceptCard(data.source, info.position, info.card, info.items_n);
2520         return true;
2521          
2522     },
2523     firstChildCard : function()
2524     {
2525         for (var i = 0;i< this.items.length;i++) {
2526             
2527             if (!this.items[i].el.hasClass('card')) {
2528                  continue;
2529             }
2530             return this.items[i];
2531         }
2532         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2533     },
2534     /**
2535      * accept card
2536      *
2537      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2538      */
2539     acceptCard : function(move_card,  position, next_to_card )
2540     {
2541         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2542             return false;
2543         }
2544         
2545         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2546         
2547         move_card.parent().removeCard(move_card);
2548         
2549         
2550         var dom = move_card.el.dom;
2551         dom.style.width = ''; // clear with - which is set by drag.
2552         
2553         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2554             var cardel = next_to_card.el.dom;
2555             
2556             if (position == 'above' ) {
2557                 cardel.parentNode.insertBefore(dom, cardel);
2558             } else if (cardel.nextSibling) {
2559                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2560             } else {
2561                 cardel.parentNode.append(dom);
2562             }
2563         } else {
2564             // card container???
2565             this.containerEl.dom.append(dom);
2566         }
2567         
2568         //FIXME HANDLE card = true 
2569         
2570         // add this to the correct place in items.
2571         
2572         // remove Card from items.
2573         
2574        
2575         if (this.items.length) {
2576             var nitems = [];
2577             //Roo.log([info.items_n, info.position, this.items.length]);
2578             for (var i =0; i < this.items.length; i++) {
2579                 if (i == to_items_n && position == 'above') {
2580                     nitems.push(move_card);
2581                 }
2582                 nitems.push(this.items[i]);
2583                 if (i == to_items_n && position == 'below') {
2584                     nitems.push(move_card);
2585                 }
2586             }
2587             this.items = nitems;
2588             Roo.log(this.items);
2589         } else {
2590             this.items.push(move_card);
2591         }
2592         
2593         move_card.parentId = this.id;
2594         
2595         return true;
2596         
2597         
2598     },
2599     removeCard : function(c)
2600     {
2601         this.items = this.items.filter(function(e) { return e != c });
2602  
2603         var dom = c.el.dom;
2604         dom.parentNode.removeChild(dom);
2605         dom.style.width = ''; // clear with - which is set by drag.
2606         c.parentId = false;
2607         
2608     },
2609     
2610     /**    Decide whether to drop above or below a View node. */
2611     getDropPoint : function(e, n, dd)
2612     {
2613         if (dd) {
2614              return false;
2615         }
2616         if (n == this.containerEl.dom) {
2617             return "above";
2618         }
2619         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2620         var c = t + (b - t) / 2;
2621         var y = Roo.lib.Event.getPageY(e);
2622         if(y <= c) {
2623             return "above";
2624         }else{
2625             return "below";
2626         }
2627     },
2628     onToggleCollapse : function(e)
2629         {
2630         if (this.collapsed) {
2631             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2632             this.collapsableEl.addClass('show');
2633             this.collapsed = false;
2634             return;
2635         }
2636         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2637         this.collapsableEl.removeClass('show');
2638         this.collapsed = true;
2639         
2640     
2641     },
2642     
2643     onToggleRotate : function(e)
2644     {
2645         this.collapsableEl.removeClass('show');
2646         this.footerEl.removeClass('d-none');
2647         this.el.removeClass('roo-card-rotated');
2648         this.el.removeClass('d-none');
2649         if (this.rotated) {
2650             
2651             this.collapsableEl.addClass('show');
2652             this.rotated = false;
2653             this.fireEvent('rotate', this, this.rotated);
2654             return;
2655         }
2656         this.el.addClass('roo-card-rotated');
2657         this.footerEl.addClass('d-none');
2658         this.el.select('.roo-collapsable').removeClass('show');
2659         
2660         this.rotated = true;
2661         this.fireEvent('rotate', this, this.rotated);
2662     
2663     },
2664     
2665     dropPlaceHolder: function (action, info, data)
2666     {
2667         if (this.dropEl === false) {
2668             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2669             cls : 'd-none'
2670             },true);
2671         }
2672         this.dropEl.removeClass(['d-none', 'd-block']);        
2673         if (action == 'hide') {
2674             
2675             this.dropEl.addClass('d-none');
2676             return;
2677         }
2678         // FIXME - info.card == true!!!
2679         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2680         
2681         if (info.card !== true) {
2682             var cardel = info.card.el.dom;
2683             
2684             if (info.position == 'above') {
2685                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2686             } else if (cardel.nextSibling) {
2687                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2688             } else {
2689                 cardel.parentNode.append(this.dropEl.dom);
2690             }
2691         } else {
2692             // card container???
2693             this.containerEl.dom.append(this.dropEl.dom);
2694         }
2695         
2696         this.dropEl.addClass('d-block roo-card-dropzone');
2697         
2698         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2699         
2700         
2701     
2702     
2703     
2704     },
2705     setHeaderText: function(html)
2706     {
2707         this.header = html;
2708         if (this.headerContainerEl) {
2709             this.headerContainerEl.dom.innerHTML = html;
2710         }
2711     },
2712     onHeaderImageLoad : function(ev, he)
2713     {
2714         if (!this.header_image_fit_square) {
2715             return;
2716         }
2717         
2718         var hw = he.naturalHeight / he.naturalWidth;
2719         // wide image = < 0
2720         // tall image = > 1
2721         //var w = he.dom.naturalWidth;
2722         var ww = he.width;
2723         he.style.left =  0;
2724         he.style.position =  'relative';
2725         if (hw > 1) {
2726             var nw = (ww * (1/hw));
2727             Roo.get(he).setSize( ww * (1/hw),  ww);
2728             he.style.left =  ((ww - nw)/ 2) + 'px';
2729             he.style.position =  'relative';
2730         }
2731
2732     }
2733
2734     
2735 });
2736
2737 /*
2738  * - LGPL
2739  *
2740  * Card header - holder for the card header elements.
2741  * 
2742  */
2743
2744 /**
2745  * @class Roo.bootstrap.CardHeader
2746  * @extends Roo.bootstrap.Element
2747  * Bootstrap CardHeader class
2748  * @constructor
2749  * Create a new Card Header - that you can embed children into
2750  * @param {Object} config The config object
2751  */
2752
2753 Roo.bootstrap.CardHeader = function(config){
2754     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2755 };
2756
2757 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2758     
2759     
2760     container_method : 'getCardHeader' 
2761     
2762      
2763     
2764     
2765    
2766 });
2767
2768  
2769
2770  /*
2771  * - LGPL
2772  *
2773  * Card footer - holder for the card footer elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardFooter
2779  * @extends Roo.bootstrap.Element
2780  * Bootstrap CardFooter class
2781  * @constructor
2782  * Create a new Card Footer - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardFooter = function(config){
2787     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardFooter' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card header - holder for the card header elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardImageTop
2812  * @extends Roo.bootstrap.Element
2813  * Bootstrap CardImageTop class
2814  * @constructor
2815  * Create a new Card Image Top container
2816  * @param {Object} config The config object
2817  */
2818
2819 Roo.bootstrap.CardImageTop = function(config){
2820     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2821 };
2822
2823 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2824     
2825    
2826     container_method : 'getCardImageTop' 
2827     
2828      
2829     
2830    
2831 });
2832
2833  
2834
2835  /*
2836  * - LGPL
2837  *
2838  * image
2839  * 
2840  */
2841
2842
2843 /**
2844  * @class Roo.bootstrap.Img
2845  * @extends Roo.bootstrap.Component
2846  * Bootstrap Img class
2847  * @cfg {Boolean} imgResponsive false | true
2848  * @cfg {String} border rounded | circle | thumbnail
2849  * @cfg {String} src image source
2850  * @cfg {String} alt image alternative text
2851  * @cfg {String} href a tag href
2852  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2853  * @cfg {String} xsUrl xs image source
2854  * @cfg {String} smUrl sm image source
2855  * @cfg {String} mdUrl md image source
2856  * @cfg {String} lgUrl lg image source
2857  * 
2858  * @constructor
2859  * Create a new Input
2860  * @param {Object} config The config object
2861  */
2862
2863 Roo.bootstrap.Img = function(config){
2864     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2865     
2866     this.addEvents({
2867         // img events
2868         /**
2869          * @event click
2870          * The img click event for the img.
2871          * @param {Roo.EventObject} e
2872          */
2873         "click" : true
2874     });
2875 };
2876
2877 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2878     
2879     imgResponsive: true,
2880     border: '',
2881     src: 'about:blank',
2882     href: false,
2883     target: false,
2884     xsUrl: '',
2885     smUrl: '',
2886     mdUrl: '',
2887     lgUrl: '',
2888
2889     getAutoCreate : function()
2890     {   
2891         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2892             return this.createSingleImg();
2893         }
2894         
2895         var cfg = {
2896             tag: 'div',
2897             cls: 'roo-image-responsive-group',
2898             cn: []
2899         };
2900         var _this = this;
2901         
2902         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2903             
2904             if(!_this[size + 'Url']){
2905                 return;
2906             }
2907             
2908             var img = {
2909                 tag: 'img',
2910                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2911                 html: _this.html || cfg.html,
2912                 src: _this[size + 'Url']
2913             };
2914             
2915             img.cls += ' roo-image-responsive-' + size;
2916             
2917             var s = ['xs', 'sm', 'md', 'lg'];
2918             
2919             s.splice(s.indexOf(size), 1);
2920             
2921             Roo.each(s, function(ss){
2922                 img.cls += ' hidden-' + ss;
2923             });
2924             
2925             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2926                 cfg.cls += ' img-' + _this.border;
2927             }
2928             
2929             if(_this.alt){
2930                 cfg.alt = _this.alt;
2931             }
2932             
2933             if(_this.href){
2934                 var a = {
2935                     tag: 'a',
2936                     href: _this.href,
2937                     cn: [
2938                         img
2939                     ]
2940                 };
2941
2942                 if(this.target){
2943                     a.target = _this.target;
2944                 }
2945             }
2946             
2947             cfg.cn.push((_this.href) ? a : img);
2948             
2949         });
2950         
2951         return cfg;
2952     },
2953     
2954     createSingleImg : function()
2955     {
2956         var cfg = {
2957             tag: 'img',
2958             cls: (this.imgResponsive) ? 'img-responsive' : '',
2959             html : null,
2960             src : 'about:blank'  // just incase src get's set to undefined?!?
2961         };
2962         
2963         cfg.html = this.html || cfg.html;
2964         
2965         cfg.src = this.src || cfg.src;
2966         
2967         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2968             cfg.cls += ' img-' + this.border;
2969         }
2970         
2971         if(this.alt){
2972             cfg.alt = this.alt;
2973         }
2974         
2975         if(this.href){
2976             var a = {
2977                 tag: 'a',
2978                 href: this.href,
2979                 cn: [
2980                     cfg
2981                 ]
2982             };
2983             
2984             if(this.target){
2985                 a.target = this.target;
2986             }
2987             
2988         }
2989         
2990         return (this.href) ? a : cfg;
2991     },
2992     
2993     initEvents: function() 
2994     {
2995         if(!this.href){
2996             this.el.on('click', this.onClick, this);
2997         }
2998         
2999     },
3000     
3001     onClick : function(e)
3002     {
3003         Roo.log('img onclick');
3004         this.fireEvent('click', this, e);
3005     },
3006     /**
3007      * Sets the url of the image - used to update it
3008      * @param {String} url the url of the image
3009      */
3010     
3011     setSrc : function(url)
3012     {
3013         this.src =  url;
3014         
3015         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3016             this.el.dom.src =  url;
3017             return;
3018         }
3019         
3020         this.el.select('img', true).first().dom.src =  url;
3021     }
3022     
3023     
3024    
3025 });
3026
3027  /*
3028  * - LGPL
3029  *
3030  * image
3031  * 
3032  */
3033
3034
3035 /**
3036  * @class Roo.bootstrap.Link
3037  * @extends Roo.bootstrap.Component
3038  * Bootstrap Link Class
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3042  * @cfg {String} html the content of the link.
3043  * @cfg {String} anchor name for the anchor link
3044  * @cfg {String} fa - favicon
3045
3046  * @cfg {Boolean} preventDefault (true | false) default false
3047
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Link = function(config){
3055     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true
3065     });
3066 };
3067
3068 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3069     
3070     href: false,
3071     target: false,
3072     preventDefault: false,
3073     anchor : false,
3074     alt : false,
3075     fa: false,
3076
3077
3078     getAutoCreate : function()
3079     {
3080         var html = this.html || '';
3081         
3082         if (this.fa !== false) {
3083             html = '<i class="fa fa-' + this.fa + '"></i>';
3084         }
3085         var cfg = {
3086             tag: 'a'
3087         };
3088         // anchor's do not require html/href...
3089         if (this.anchor === false) {
3090             cfg.html = html;
3091             cfg.href = this.href || '#';
3092         } else {
3093             cfg.name = this.anchor;
3094             if (this.html !== false || this.fa !== false) {
3095                 cfg.html = html;
3096             }
3097             if (this.href !== false) {
3098                 cfg.href = this.href;
3099             }
3100         }
3101         
3102         if(this.alt !== false){
3103             cfg.alt = this.alt;
3104         }
3105         
3106         
3107         if(this.target !== false) {
3108             cfg.target = this.target;
3109         }
3110         
3111         return cfg;
3112     },
3113     
3114     initEvents: function() {
3115         
3116         if(!this.href || this.preventDefault){
3117             this.el.on('click', this.onClick, this);
3118         }
3119     },
3120     
3121     onClick : function(e)
3122     {
3123         if(this.preventDefault){
3124             e.preventDefault();
3125         }
3126         //Roo.log('img onclick');
3127         this.fireEvent('click', this, e);
3128     }
3129    
3130 });
3131
3132  /*
3133  * - LGPL
3134  *
3135  * header
3136  * 
3137  */
3138
3139 /**
3140  * @class Roo.bootstrap.Header
3141  * @extends Roo.bootstrap.Component
3142  * Bootstrap Header class
3143  * @cfg {String} html content of header
3144  * @cfg {Number} level (1|2|3|4|5|6) default 1
3145  * 
3146  * @constructor
3147  * Create a new Header
3148  * @param {Object} config The config object
3149  */
3150
3151
3152 Roo.bootstrap.Header  = function(config){
3153     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3154 };
3155
3156 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3157     
3158     //href : false,
3159     html : false,
3160     level : 1,
3161     
3162     
3163     
3164     getAutoCreate : function(){
3165         
3166         
3167         
3168         var cfg = {
3169             tag: 'h' + (1 *this.level),
3170             html: this.html || ''
3171         } ;
3172         
3173         return cfg;
3174     }
3175    
3176 });
3177
3178  
3179
3180  /*
3181  * Based on:
3182  * Ext JS Library 1.1.1
3183  * Copyright(c) 2006-2007, Ext JS, LLC.
3184  *
3185  * Originally Released Under LGPL - original licence link has changed is not relivant.
3186  *
3187  * Fork - LGPL
3188  * <script type="text/javascript">
3189  */
3190  
3191 /**
3192  * @class Roo.bootstrap.MenuMgr
3193  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3194  * @singleton
3195  */
3196 Roo.bootstrap.MenuMgr = function(){
3197    var menus, active, groups = {}, attached = false, lastShow = new Date();
3198
3199    // private - called when first menu is created
3200    function init(){
3201        menus = {};
3202        active = new Roo.util.MixedCollection();
3203        Roo.get(document).addKeyListener(27, function(){
3204            if(active.length > 0){
3205                hideAll();
3206            }
3207        });
3208    }
3209
3210    // private
3211    function hideAll(){
3212        if(active && active.length > 0){
3213            var c = active.clone();
3214            c.each(function(m){
3215                m.hide();
3216            });
3217        }
3218    }
3219
3220    // private
3221    function onHide(m){
3222        active.remove(m);
3223        if(active.length < 1){
3224            Roo.get(document).un("mouseup", onMouseDown);
3225             
3226            attached = false;
3227        }
3228    }
3229
3230    // private
3231    function onShow(m){
3232        var last = active.last();
3233        lastShow = new Date();
3234        active.add(m);
3235        if(!attached){
3236           Roo.get(document).on("mouseup", onMouseDown);
3237            
3238            attached = true;
3239        }
3240        if(m.parentMenu){
3241           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3242           m.parentMenu.activeChild = m;
3243        }else if(last && last.isVisible()){
3244           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3245        }
3246    }
3247
3248    // private
3249    function onBeforeHide(m){
3250        if(m.activeChild){
3251            m.activeChild.hide();
3252        }
3253        if(m.autoHideTimer){
3254            clearTimeout(m.autoHideTimer);
3255            delete m.autoHideTimer;
3256        }
3257    }
3258
3259    // private
3260    function onBeforeShow(m){
3261        var pm = m.parentMenu;
3262        if(!pm && !m.allowOtherMenus){
3263            hideAll();
3264        }else if(pm && pm.activeChild && active != m){
3265            pm.activeChild.hide();
3266        }
3267    }
3268
3269    // private this should really trigger on mouseup..
3270    function onMouseDown(e){
3271         Roo.log("on Mouse Up");
3272         
3273         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3274             Roo.log("MenuManager hideAll");
3275             hideAll();
3276             e.stopEvent();
3277         }
3278         
3279         
3280    }
3281
3282    // private
3283    function onBeforeCheck(mi, state){
3284        if(state){
3285            var g = groups[mi.group];
3286            for(var i = 0, l = g.length; i < l; i++){
3287                if(g[i] != mi){
3288                    g[i].setChecked(false);
3289                }
3290            }
3291        }
3292    }
3293
3294    return {
3295
3296        /**
3297         * Hides all menus that are currently visible
3298         */
3299        hideAll : function(){
3300             hideAll();  
3301        },
3302
3303        // private
3304        register : function(menu){
3305            if(!menus){
3306                init();
3307            }
3308            menus[menu.id] = menu;
3309            menu.on("beforehide", onBeforeHide);
3310            menu.on("hide", onHide);
3311            menu.on("beforeshow", onBeforeShow);
3312            menu.on("show", onShow);
3313            var g = menu.group;
3314            if(g && menu.events["checkchange"]){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menu);
3319                menu.on("checkchange", onCheck);
3320            }
3321        },
3322
3323         /**
3324          * Returns a {@link Roo.menu.Menu} object
3325          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3326          * be used to generate and return a new Menu instance.
3327          */
3328        get : function(menu){
3329            if(typeof menu == "string"){ // menu id
3330                return menus[menu];
3331            }else if(menu.events){  // menu instance
3332                return menu;
3333            }
3334            /*else if(typeof menu.length == 'number'){ // array of menu items?
3335                return new Roo.bootstrap.Menu({items:menu});
3336            }else{ // otherwise, must be a config
3337                return new Roo.bootstrap.Menu(menu);
3338            }
3339            */
3340            return false;
3341        },
3342
3343        // private
3344        unregister : function(menu){
3345            delete menus[menu.id];
3346            menu.un("beforehide", onBeforeHide);
3347            menu.un("hide", onHide);
3348            menu.un("beforeshow", onBeforeShow);
3349            menu.un("show", onShow);
3350            var g = menu.group;
3351            if(g && menu.events["checkchange"]){
3352                groups[g].remove(menu);
3353                menu.un("checkchange", onCheck);
3354            }
3355        },
3356
3357        // private
3358        registerCheckable : function(menuItem){
3359            var g = menuItem.group;
3360            if(g){
3361                if(!groups[g]){
3362                    groups[g] = [];
3363                }
3364                groups[g].push(menuItem);
3365                menuItem.on("beforecheckchange", onBeforeCheck);
3366            }
3367        },
3368
3369        // private
3370        unregisterCheckable : function(menuItem){
3371            var g = menuItem.group;
3372            if(g){
3373                groups[g].remove(menuItem);
3374                menuItem.un("beforecheckchange", onBeforeCheck);
3375            }
3376        }
3377    };
3378 }();/*
3379  * - LGPL
3380  *
3381  * menu
3382  * 
3383  */
3384
3385 /**
3386  * @class Roo.bootstrap.Menu
3387  * @extends Roo.bootstrap.Component
3388  * Bootstrap Menu class - container for MenuItems
3389  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3390  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3391  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3392  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3393  * 
3394  * @constructor
3395  * Create a new Menu
3396  * @param {Object} config The config object
3397  */
3398
3399
3400 Roo.bootstrap.Menu = function(config){
3401     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3402     if (this.registerMenu && this.type != 'treeview')  {
3403         Roo.bootstrap.MenuMgr.register(this);
3404     }
3405     
3406     
3407     this.addEvents({
3408         /**
3409          * @event beforeshow
3410          * Fires before this menu is displayed (return false to block)
3411          * @param {Roo.menu.Menu} this
3412          */
3413         beforeshow : true,
3414         /**
3415          * @event beforehide
3416          * Fires before this menu is hidden (return false to block)
3417          * @param {Roo.menu.Menu} this
3418          */
3419         beforehide : true,
3420         /**
3421          * @event show
3422          * Fires after this menu is displayed
3423          * @param {Roo.menu.Menu} this
3424          */
3425         show : true,
3426         /**
3427          * @event hide
3428          * Fires after this menu is hidden
3429          * @param {Roo.menu.Menu} this
3430          */
3431         hide : true,
3432         /**
3433          * @event click
3434          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3435          * @param {Roo.menu.Menu} this
3436          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3437          * @param {Roo.EventObject} e
3438          */
3439         click : true,
3440         /**
3441          * @event mouseover
3442          * Fires when the mouse is hovering over this menu
3443          * @param {Roo.menu.Menu} this
3444          * @param {Roo.EventObject} e
3445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3446          */
3447         mouseover : true,
3448         /**
3449          * @event mouseout
3450          * Fires when the mouse exits this menu
3451          * @param {Roo.menu.Menu} this
3452          * @param {Roo.EventObject} e
3453          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3454          */
3455         mouseout : true,
3456         /**
3457          * @event itemclick
3458          * Fires when a menu item contained in this menu is clicked
3459          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3460          * @param {Roo.EventObject} e
3461          */
3462         itemclick: true
3463     });
3464     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3465 };
3466
3467 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3468     
3469    /// html : false,
3470     //align : '',
3471     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3472     type: false,
3473     /**
3474      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3475      */
3476     registerMenu : true,
3477     
3478     menuItems :false, // stores the menu items..
3479     
3480     hidden:true,
3481         
3482     parentMenu : false,
3483     
3484     stopEvent : true,
3485     
3486     isLink : false,
3487     
3488     getChildContainer : function() {
3489         return this.el;  
3490     },
3491     
3492     getAutoCreate : function(){
3493          
3494         //if (['right'].indexOf(this.align)!==-1) {
3495         //    cfg.cn[1].cls += ' pull-right'
3496         //}
3497         
3498         
3499         var cfg = {
3500             tag : 'ul',
3501             cls : 'dropdown-menu' ,
3502             style : 'z-index:1000'
3503             
3504         };
3505         
3506         if (this.type === 'submenu') {
3507             cfg.cls = 'submenu active';
3508         }
3509         if (this.type === 'treeview') {
3510             cfg.cls = 'treeview-menu';
3511         }
3512         
3513         return cfg;
3514     },
3515     initEvents : function() {
3516         
3517        // Roo.log("ADD event");
3518        // Roo.log(this.triggerEl.dom);
3519         
3520         this.triggerEl.on('click', this.onTriggerClick, this);
3521         
3522         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3523         
3524         
3525         if (this.triggerEl.hasClass('nav-item')) {
3526             // dropdown toggle on the 'a' in BS4?
3527             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3528         } else {
3529             this.triggerEl.addClass('dropdown-toggle');
3530         }
3531         if (Roo.isTouch) {
3532             this.el.on('touchstart'  , this.onTouch, this);
3533         }
3534         this.el.on('click' , this.onClick, this);
3535
3536         this.el.on("mouseover", this.onMouseOver, this);
3537         this.el.on("mouseout", this.onMouseOut, this);
3538         
3539     },
3540     
3541     findTargetItem : function(e)
3542     {
3543         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3544         if(!t){
3545             return false;
3546         }
3547         //Roo.log(t);         Roo.log(t.id);
3548         if(t && t.id){
3549             //Roo.log(this.menuitems);
3550             return this.menuitems.get(t.id);
3551             
3552             //return this.items.get(t.menuItemId);
3553         }
3554         
3555         return false;
3556     },
3557     
3558     onTouch : function(e) 
3559     {
3560         Roo.log("menu.onTouch");
3561         //e.stopEvent(); this make the user popdown broken
3562         this.onClick(e);
3563     },
3564     
3565     onClick : function(e)
3566     {
3567         Roo.log("menu.onClick");
3568         
3569         var t = this.findTargetItem(e);
3570         if(!t || t.isContainer){
3571             return;
3572         }
3573         Roo.log(e);
3574         /*
3575         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3576             if(t == this.activeItem && t.shouldDeactivate(e)){
3577                 this.activeItem.deactivate();
3578                 delete this.activeItem;
3579                 return;
3580             }
3581             if(t.canActivate){
3582                 this.setActiveItem(t, true);
3583             }
3584             return;
3585             
3586             
3587         }
3588         */
3589        
3590         Roo.log('pass click event');
3591         
3592         t.onClick(e);
3593         
3594         this.fireEvent("click", this, t, e);
3595         
3596         var _this = this;
3597         
3598         if(!t.href.length || t.href == '#'){
3599             (function() { _this.hide(); }).defer(100);
3600         }
3601         
3602     },
3603     
3604     onMouseOver : function(e){
3605         var t  = this.findTargetItem(e);
3606         //Roo.log(t);
3607         //if(t){
3608         //    if(t.canActivate && !t.disabled){
3609         //        this.setActiveItem(t, true);
3610         //    }
3611         //}
3612         
3613         this.fireEvent("mouseover", this, e, t);
3614     },
3615     isVisible : function(){
3616         return !this.hidden;
3617     },
3618     onMouseOut : function(e){
3619         var t  = this.findTargetItem(e);
3620         
3621         //if(t ){
3622         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3623         //        this.activeItem.deactivate();
3624         //        delete this.activeItem;
3625         //    }
3626         //}
3627         this.fireEvent("mouseout", this, e, t);
3628     },
3629     
3630     
3631     /**
3632      * Displays this menu relative to another element
3633      * @param {String/HTMLElement/Roo.Element} element The element to align to
3634      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3635      * the element (defaults to this.defaultAlign)
3636      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3637      */
3638     show : function(el, pos, parentMenu)
3639     {
3640         if (false === this.fireEvent("beforeshow", this)) {
3641             Roo.log("show canceled");
3642             return;
3643         }
3644         this.parentMenu = parentMenu;
3645         if(!this.el){
3646             this.render();
3647         }
3648         
3649         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3650     },
3651      /**
3652      * Displays this menu at a specific xy position
3653      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3654      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3655      */
3656     showAt : function(xy, parentMenu, /* private: */_e){
3657         this.parentMenu = parentMenu;
3658         if(!this.el){
3659             this.render();
3660         }
3661         if(_e !== false){
3662             this.fireEvent("beforeshow", this);
3663             //xy = this.el.adjustForConstraints(xy);
3664         }
3665         
3666         //this.el.show();
3667         this.hideMenuItems();
3668         this.hidden = false;
3669         this.triggerEl.addClass('open');
3670         this.el.addClass('show');
3671         
3672         // reassign x when hitting right
3673         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3674             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3675         }
3676         
3677         // reassign y when hitting bottom
3678         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3679             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3680         }
3681         
3682         // but the list may align on trigger left or trigger top... should it be a properity?
3683         
3684         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3685             this.el.setXY(xy);
3686         }
3687         
3688         this.focus();
3689         this.fireEvent("show", this);
3690     },
3691     
3692     focus : function(){
3693         return;
3694         if(!this.hidden){
3695             this.doFocus.defer(50, this);
3696         }
3697     },
3698
3699     doFocus : function(){
3700         if(!this.hidden){
3701             this.focusEl.focus();
3702         }
3703     },
3704
3705     /**
3706      * Hides this menu and optionally all parent menus
3707      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3708      */
3709     hide : function(deep)
3710     {
3711         if (false === this.fireEvent("beforehide", this)) {
3712             Roo.log("hide canceled");
3713             return;
3714         }
3715         this.hideMenuItems();
3716         if(this.el && this.isVisible()){
3717            
3718             if(this.activeItem){
3719                 this.activeItem.deactivate();
3720                 this.activeItem = null;
3721             }
3722             this.triggerEl.removeClass('open');;
3723             this.el.removeClass('show');
3724             this.hidden = true;
3725             this.fireEvent("hide", this);
3726         }
3727         if(deep === true && this.parentMenu){
3728             this.parentMenu.hide(true);
3729         }
3730     },
3731     
3732     onTriggerClick : function(e)
3733     {
3734         Roo.log('trigger click');
3735         
3736         var target = e.getTarget();
3737         
3738         Roo.log(target.nodeName.toLowerCase());
3739         
3740         if(target.nodeName.toLowerCase() === 'i'){
3741             e.preventDefault();
3742         }
3743         
3744     },
3745     
3746     onTriggerPress  : function(e)
3747     {
3748         Roo.log('trigger press');
3749         //Roo.log(e.getTarget());
3750        // Roo.log(this.triggerEl.dom);
3751        
3752         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3753         var pel = Roo.get(e.getTarget());
3754         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3755             Roo.log('is treeview or dropdown?');
3756             return;
3757         }
3758         
3759         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3760             return;
3761         }
3762         
3763         if (this.isVisible()) {
3764             Roo.log('hide');
3765             this.hide();
3766         } else {
3767             Roo.log('show');
3768             this.show(this.triggerEl, '?', false);
3769         }
3770         
3771         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3772             e.stopEvent();
3773         }
3774         
3775     },
3776        
3777     
3778     hideMenuItems : function()
3779     {
3780         Roo.log("hide Menu Items");
3781         if (!this.el) { 
3782             return;
3783         }
3784         
3785         this.el.select('.open',true).each(function(aa) {
3786             
3787             aa.removeClass('open');
3788          
3789         });
3790     },
3791     addxtypeChild : function (tree, cntr) {
3792         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3793           
3794         this.menuitems.add(comp);
3795         return comp;
3796
3797     },
3798     getEl : function()
3799     {
3800         Roo.log(this.el);
3801         return this.el;
3802     },
3803     
3804     clear : function()
3805     {
3806         this.getEl().dom.innerHTML = '';
3807         this.menuitems.clear();
3808     }
3809 });
3810
3811  
3812  /*
3813  * - LGPL
3814  *
3815  * menu item
3816  * 
3817  */
3818
3819
3820 /**
3821  * @class Roo.bootstrap.MenuItem
3822  * @extends Roo.bootstrap.Component
3823  * Bootstrap MenuItem class
3824  * @cfg {String} html the menu label
3825  * @cfg {String} href the link
3826  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3827  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3828  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3829  * @cfg {String} fa favicon to show on left of menu item.
3830  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3831  * 
3832  * 
3833  * @constructor
3834  * Create a new MenuItem
3835  * @param {Object} config The config object
3836  */
3837
3838
3839 Roo.bootstrap.MenuItem = function(config){
3840     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3841     this.addEvents({
3842         // raw events
3843         /**
3844          * @event click
3845          * The raw click event for the entire grid.
3846          * @param {Roo.bootstrap.MenuItem} this
3847          * @param {Roo.EventObject} e
3848          */
3849         "click" : true
3850     });
3851 };
3852
3853 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3854     
3855     href : false,
3856     html : false,
3857     preventDefault: false,
3858     isContainer : false,
3859     active : false,
3860     fa: false,
3861     
3862     getAutoCreate : function(){
3863         
3864         if(this.isContainer){
3865             return {
3866                 tag: 'li',
3867                 cls: 'dropdown-menu-item '
3868             };
3869         }
3870         var ctag = {
3871             tag: 'span',
3872             html: 'Link'
3873         };
3874         
3875         var anc = {
3876             tag : 'a',
3877             cls : 'dropdown-item',
3878             href : '#',
3879             cn : [  ]
3880         };
3881         
3882         if (this.fa !== false) {
3883             anc.cn.push({
3884                 tag : 'i',
3885                 cls : 'fa fa-' + this.fa
3886             });
3887         }
3888         
3889         anc.cn.push(ctag);
3890         
3891         
3892         var cfg= {
3893             tag: 'li',
3894             cls: 'dropdown-menu-item',
3895             cn: [ anc ]
3896         };
3897         if (this.parent().type == 'treeview') {
3898             cfg.cls = 'treeview-menu';
3899         }
3900         if (this.active) {
3901             cfg.cls += ' active';
3902         }
3903         
3904         
3905         
3906         anc.href = this.href || cfg.cn[0].href ;
3907         ctag.html = this.html || cfg.cn[0].html ;
3908         return cfg;
3909     },
3910     
3911     initEvents: function()
3912     {
3913         if (this.parent().type == 'treeview') {
3914             this.el.select('a').on('click', this.onClick, this);
3915         }
3916         
3917         if (this.menu) {
3918             this.menu.parentType = this.xtype;
3919             this.menu.triggerEl = this.el;
3920             this.menu = this.addxtype(Roo.apply({}, this.menu));
3921         }
3922         
3923     },
3924     onClick : function(e)
3925     {
3926         Roo.log('item on click ');
3927         
3928         if(this.preventDefault){
3929             e.preventDefault();
3930         }
3931         //this.parent().hideMenuItems();
3932         
3933         this.fireEvent('click', this, e);
3934     },
3935     getEl : function()
3936     {
3937         return this.el;
3938     } 
3939 });
3940
3941  
3942
3943  /*
3944  * - LGPL
3945  *
3946  * menu separator
3947  * 
3948  */
3949
3950
3951 /**
3952  * @class Roo.bootstrap.MenuSeparator
3953  * @extends Roo.bootstrap.Component
3954  * Bootstrap MenuSeparator class
3955  * 
3956  * @constructor
3957  * Create a new MenuItem
3958  * @param {Object} config The config object
3959  */
3960
3961
3962 Roo.bootstrap.MenuSeparator = function(config){
3963     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3964 };
3965
3966 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3967     
3968     getAutoCreate : function(){
3969         var cfg = {
3970             cls: 'divider',
3971             tag : 'li'
3972         };
3973         
3974         return cfg;
3975     }
3976    
3977 });
3978
3979  
3980
3981  
3982 /*
3983 * Licence: LGPL
3984 */
3985
3986 /**
3987  * @class Roo.bootstrap.Modal
3988  * @extends Roo.bootstrap.Component
3989  * Bootstrap Modal class
3990  * @cfg {String} title Title of dialog
3991  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3992  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3993  * @cfg {Boolean} specificTitle default false
3994  * @cfg {Array} buttons Array of buttons or standard button set..
3995  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3996  * @cfg {Boolean} animate default true
3997  * @cfg {Boolean} allow_close default true
3998  * @cfg {Boolean} fitwindow default false
3999  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4000  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4001  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4002  * @cfg {String} size (sm|lg|xl) default empty
4003  * @cfg {Number} max_width set the max width of modal
4004  * @cfg {Boolean} editableTitle can the title be edited
4005
4006  *
4007  *
4008  * @constructor
4009  * Create a new Modal Dialog
4010  * @param {Object} config The config object
4011  */
4012
4013 Roo.bootstrap.Modal = function(config){
4014     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4015     this.addEvents({
4016         // raw events
4017         /**
4018          * @event btnclick
4019          * The raw btnclick event for the button
4020          * @param {Roo.EventObject} e
4021          */
4022         "btnclick" : true,
4023         /**
4024          * @event resize
4025          * Fire when dialog resize
4026          * @param {Roo.bootstrap.Modal} this
4027          * @param {Roo.EventObject} e
4028          */
4029         "resize" : true,
4030         /**
4031          * @event titlechanged
4032          * Fire when the editable title has been changed
4033          * @param {Roo.bootstrap.Modal} this
4034          * @param {Roo.EventObject} value
4035          */
4036         "titlechanged" : true 
4037         
4038     });
4039     this.buttons = this.buttons || [];
4040
4041     if (this.tmpl) {
4042         this.tmpl = Roo.factory(this.tmpl);
4043     }
4044
4045 };
4046
4047 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4048
4049     title : 'test dialog',
4050
4051     buttons : false,
4052
4053     // set on load...
4054
4055     html: false,
4056
4057     tmp: false,
4058
4059     specificTitle: false,
4060
4061     buttonPosition: 'right',
4062
4063     allow_close : true,
4064
4065     animate : true,
4066
4067     fitwindow: false,
4068     
4069      // private
4070     dialogEl: false,
4071     bodyEl:  false,
4072     footerEl:  false,
4073     titleEl:  false,
4074     closeEl:  false,
4075
4076     size: '',
4077     
4078     max_width: 0,
4079     
4080     max_height: 0,
4081     
4082     fit_content: false,
4083     editableTitle  : false,
4084
4085     onRender : function(ct, position)
4086     {
4087         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4088
4089         if(!this.el){
4090             var cfg = Roo.apply({},  this.getAutoCreate());
4091             cfg.id = Roo.id();
4092             //if(!cfg.name){
4093             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4094             //}
4095             //if (!cfg.name.length) {
4096             //    delete cfg.name;
4097            // }
4098             if (this.cls) {
4099                 cfg.cls += ' ' + this.cls;
4100             }
4101             if (this.style) {
4102                 cfg.style = this.style;
4103             }
4104             this.el = Roo.get(document.body).createChild(cfg, position);
4105         }
4106         //var type = this.el.dom.type;
4107
4108
4109         if(this.tabIndex !== undefined){
4110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4111         }
4112
4113         this.dialogEl = this.el.select('.modal-dialog',true).first();
4114         this.bodyEl = this.el.select('.modal-body',true).first();
4115         this.closeEl = this.el.select('.modal-header .close', true).first();
4116         this.headerEl = this.el.select('.modal-header',true).first();
4117         this.titleEl = this.el.select('.modal-title',true).first();
4118         this.footerEl = this.el.select('.modal-footer',true).first();
4119
4120         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4121         
4122         //this.el.addClass("x-dlg-modal");
4123
4124         if (this.buttons.length) {
4125             Roo.each(this.buttons, function(bb) {
4126                 var b = Roo.apply({}, bb);
4127                 b.xns = b.xns || Roo.bootstrap;
4128                 b.xtype = b.xtype || 'Button';
4129                 if (typeof(b.listeners) == 'undefined') {
4130                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4131                 }
4132
4133                 var btn = Roo.factory(b);
4134
4135                 btn.render(this.getButtonContainer());
4136
4137             },this);
4138         }
4139         // render the children.
4140         var nitems = [];
4141
4142         if(typeof(this.items) != 'undefined'){
4143             var items = this.items;
4144             delete this.items;
4145
4146             for(var i =0;i < items.length;i++) {
4147                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4148             }
4149         }
4150
4151         this.items = nitems;
4152
4153         // where are these used - they used to be body/close/footer
4154
4155
4156         this.initEvents();
4157         //this.el.addClass([this.fieldClass, this.cls]);
4158
4159     },
4160
4161     getAutoCreate : function()
4162     {
4163         // we will default to modal-body-overflow - might need to remove or make optional later.
4164         var bdy = {
4165                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4166                 html : this.html || ''
4167         };
4168
4169         var title = {
4170             tag: 'h5',
4171             cls : 'modal-title',
4172             html : this.title
4173         };
4174
4175         if(this.specificTitle){ // WTF is this?
4176             title = this.title;
4177         }
4178
4179         var header = [];
4180         if (this.allow_close && Roo.bootstrap.version == 3) {
4181             header.push({
4182                 tag: 'button',
4183                 cls : 'close',
4184                 html : '&times'
4185             });
4186         }
4187
4188         header.push(title);
4189
4190         if (this.editableTitle) {
4191             header.push({
4192                 cls: 'form-control roo-editable-title d-none',
4193                 tag: 'input',
4194                 type: 'text'
4195             });
4196         }
4197         
4198         if (this.allow_close && Roo.bootstrap.version == 4) {
4199             header.push({
4200                 tag: 'button',
4201                 cls : 'close',
4202                 html : '&times'
4203             });
4204         }
4205         
4206         var size = '';
4207
4208         if(this.size.length){
4209             size = 'modal-' + this.size;
4210         }
4211         
4212         var footer = Roo.bootstrap.version == 3 ?
4213             {
4214                 cls : 'modal-footer',
4215                 cn : [
4216                     {
4217                         tag: 'div',
4218                         cls: 'btn-' + this.buttonPosition
4219                     }
4220                 ]
4221
4222             } :
4223             {  // BS4 uses mr-auto on left buttons....
4224                 cls : 'modal-footer'
4225             };
4226
4227             
4228
4229         
4230         
4231         var modal = {
4232             cls: "modal",
4233              cn : [
4234                 {
4235                     cls: "modal-dialog " + size,
4236                     cn : [
4237                         {
4238                             cls : "modal-content",
4239                             cn : [
4240                                 {
4241                                     cls : 'modal-header',
4242                                     cn : header
4243                                 },
4244                                 bdy,
4245                                 footer
4246                             ]
4247
4248                         }
4249                     ]
4250
4251                 }
4252             ]
4253         };
4254
4255         if(this.animate){
4256             modal.cls += ' fade';
4257         }
4258
4259         return modal;
4260
4261     },
4262     getChildContainer : function() {
4263
4264          return this.bodyEl;
4265
4266     },
4267     getButtonContainer : function() {
4268         
4269          return Roo.bootstrap.version == 4 ?
4270             this.el.select('.modal-footer',true).first()
4271             : this.el.select('.modal-footer div',true).first();
4272
4273     },
4274     initEvents : function()
4275     {
4276         if (this.allow_close) {
4277             this.closeEl.on('click', this.hide, this);
4278         }
4279         Roo.EventManager.onWindowResize(this.resize, this, true);
4280         if (this.editableTitle) {
4281             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4282             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4283             this.headerEditEl.on('keyup', function(e) {
4284                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4285                         this.toggleHeaderInput(false)
4286                     }
4287                 }, this);
4288             this.headerEditEl.on('blur', function(e) {
4289                 this.toggleHeaderInput(false)
4290             },this);
4291         }
4292
4293     },
4294   
4295
4296     resize : function()
4297     {
4298         this.maskEl.setSize(
4299             Roo.lib.Dom.getViewWidth(true),
4300             Roo.lib.Dom.getViewHeight(true)
4301         );
4302         
4303         if (this.fitwindow) {
4304             
4305            this.dialogEl.setStyle( { 'max-width' : '100%' });
4306             this.setSize(
4307                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4308                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4309             );
4310             return;
4311         }
4312         
4313         if(this.max_width !== 0) {
4314             
4315             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4316             
4317             if(this.height) {
4318                 this.setSize(w, this.height);
4319                 return;
4320             }
4321             
4322             if(this.max_height) {
4323                 this.setSize(w,Math.min(
4324                     this.max_height,
4325                     Roo.lib.Dom.getViewportHeight(true) - 60
4326                 ));
4327                 
4328                 return;
4329             }
4330             
4331             if(!this.fit_content) {
4332                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4333                 return;
4334             }
4335             
4336             this.setSize(w, Math.min(
4337                 60 +
4338                 this.headerEl.getHeight() + 
4339                 this.footerEl.getHeight() + 
4340                 this.getChildHeight(this.bodyEl.dom.childNodes),
4341                 Roo.lib.Dom.getViewportHeight(true) - 60)
4342             );
4343         }
4344         
4345     },
4346
4347     setSize : function(w,h)
4348     {
4349         if (!w && !h) {
4350             return;
4351         }
4352         
4353         this.resizeTo(w,h);
4354     },
4355
4356     show : function() {
4357
4358         if (!this.rendered) {
4359             this.render();
4360         }
4361         this.toggleHeaderInput(false);
4362         //this.el.setStyle('display', 'block');
4363         this.el.removeClass('hideing');
4364         this.el.dom.style.display='block';
4365         
4366         Roo.get(document.body).addClass('modal-open');
4367  
4368         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4369             
4370             (function(){
4371                 this.el.addClass('show');
4372                 this.el.addClass('in');
4373             }).defer(50, this);
4374         }else{
4375             this.el.addClass('show');
4376             this.el.addClass('in');
4377         }
4378
4379         // not sure how we can show data in here..
4380         //if (this.tmpl) {
4381         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4382         //}
4383
4384         Roo.get(document.body).addClass("x-body-masked");
4385         
4386         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4387         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4388         this.maskEl.dom.style.display = 'block';
4389         this.maskEl.addClass('show');
4390         
4391         
4392         this.resize();
4393         
4394         this.fireEvent('show', this);
4395
4396         // set zindex here - otherwise it appears to be ignored...
4397         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4398
4399         (function () {
4400             this.items.forEach( function(e) {
4401                 e.layout ? e.layout() : false;
4402
4403             });
4404         }).defer(100,this);
4405
4406     },
4407     hide : function()
4408     {
4409         if(this.fireEvent("beforehide", this) !== false){
4410             
4411             this.maskEl.removeClass('show');
4412             
4413             this.maskEl.dom.style.display = '';
4414             Roo.get(document.body).removeClass("x-body-masked");
4415             this.el.removeClass('in');
4416             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4417
4418             if(this.animate){ // why
4419                 this.el.addClass('hideing');
4420                 this.el.removeClass('show');
4421                 (function(){
4422                     if (!this.el.hasClass('hideing')) {
4423                         return; // it's been shown again...
4424                     }
4425                     
4426                     this.el.dom.style.display='';
4427
4428                     Roo.get(document.body).removeClass('modal-open');
4429                     this.el.removeClass('hideing');
4430                 }).defer(150,this);
4431                 
4432             }else{
4433                 this.el.removeClass('show');
4434                 this.el.dom.style.display='';
4435                 Roo.get(document.body).removeClass('modal-open');
4436
4437             }
4438             this.fireEvent('hide', this);
4439         }
4440     },
4441     isVisible : function()
4442     {
4443         
4444         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4445         
4446     },
4447
4448     addButton : function(str, cb)
4449     {
4450
4451
4452         var b = Roo.apply({}, { html : str } );
4453         b.xns = b.xns || Roo.bootstrap;
4454         b.xtype = b.xtype || 'Button';
4455         if (typeof(b.listeners) == 'undefined') {
4456             b.listeners = { click : cb.createDelegate(this)  };
4457         }
4458
4459         var btn = Roo.factory(b);
4460
4461         btn.render(this.getButtonContainer());
4462
4463         return btn;
4464
4465     },
4466
4467     setDefaultButton : function(btn)
4468     {
4469         //this.el.select('.modal-footer').()
4470     },
4471
4472     resizeTo: function(w,h)
4473     {
4474         this.dialogEl.setWidth(w);
4475         
4476         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4477
4478         this.bodyEl.setHeight(h - diff);
4479         
4480         this.fireEvent('resize', this);
4481     },
4482     
4483     setContentSize  : function(w, h)
4484     {
4485
4486     },
4487     onButtonClick: function(btn,e)
4488     {
4489         //Roo.log([a,b,c]);
4490         this.fireEvent('btnclick', btn.name, e);
4491     },
4492      /**
4493      * Set the title of the Dialog
4494      * @param {String} str new Title
4495      */
4496     setTitle: function(str) {
4497         this.titleEl.dom.innerHTML = str;
4498         this.title = str;
4499     },
4500     /**
4501      * Set the body of the Dialog
4502      * @param {String} str new Title
4503      */
4504     setBody: function(str) {
4505         this.bodyEl.dom.innerHTML = str;
4506     },
4507     /**
4508      * Set the body of the Dialog using the template
4509      * @param {Obj} data - apply this data to the template and replace the body contents.
4510      */
4511     applyBody: function(obj)
4512     {
4513         if (!this.tmpl) {
4514             Roo.log("Error - using apply Body without a template");
4515             //code
4516         }
4517         this.tmpl.overwrite(this.bodyEl, obj);
4518     },
4519     
4520     getChildHeight : function(child_nodes)
4521     {
4522         if(
4523             !child_nodes ||
4524             child_nodes.length == 0
4525         ) {
4526             return 0;
4527         }
4528         
4529         var child_height = 0;
4530         
4531         for(var i = 0; i < child_nodes.length; i++) {
4532             
4533             /*
4534             * for modal with tabs...
4535             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4536                 
4537                 var layout_childs = child_nodes[i].childNodes;
4538                 
4539                 for(var j = 0; j < layout_childs.length; j++) {
4540                     
4541                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4542                         
4543                         var layout_body_childs = layout_childs[j].childNodes;
4544                         
4545                         for(var k = 0; k < layout_body_childs.length; k++) {
4546                             
4547                             if(layout_body_childs[k].classList.contains('navbar')) {
4548                                 child_height += layout_body_childs[k].offsetHeight;
4549                                 continue;
4550                             }
4551                             
4552                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4553                                 
4554                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4555                                 
4556                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4557                                     
4558                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4559                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4560                                         continue;
4561                                     }
4562                                     
4563                                 }
4564                                 
4565                             }
4566                             
4567                         }
4568                     }
4569                 }
4570                 continue;
4571             }
4572             */
4573             
4574             child_height += child_nodes[i].offsetHeight;
4575             // Roo.log(child_nodes[i].offsetHeight);
4576         }
4577         
4578         return child_height;
4579     },
4580     toggleHeaderInput : function(is_edit)
4581     {
4582         if (!this.editableTitle) {
4583             return; // not editable.
4584         }
4585         if (is_edit && this.is_header_editing) {
4586             return; // already editing..
4587         }
4588         if (is_edit) {
4589     
4590             this.headerEditEl.dom.value = this.title;
4591             this.headerEditEl.removeClass('d-none');
4592             this.headerEditEl.dom.focus();
4593             this.titleEl.addClass('d-none');
4594             
4595             this.is_header_editing = true;
4596             return
4597         }
4598         // flip back to not editing.
4599         this.title = this.headerEditEl.dom.value;
4600         this.headerEditEl.addClass('d-none');
4601         this.titleEl.removeClass('d-none');
4602         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4603         this.is_header_editing = false;
4604         this.fireEvent('titlechanged', this, this.title);
4605     
4606             
4607         
4608     }
4609
4610 });
4611
4612
4613 Roo.apply(Roo.bootstrap.Modal,  {
4614     /**
4615          * Button config that displays a single OK button
4616          * @type Object
4617          */
4618         OK :  [{
4619             name : 'ok',
4620             weight : 'primary',
4621             html : 'OK'
4622         }],
4623         /**
4624          * Button config that displays Yes and No buttons
4625          * @type Object
4626          */
4627         YESNO : [
4628             {
4629                 name  : 'no',
4630                 html : 'No'
4631             },
4632             {
4633                 name  :'yes',
4634                 weight : 'primary',
4635                 html : 'Yes'
4636             }
4637         ],
4638
4639         /**
4640          * Button config that displays OK and Cancel buttons
4641          * @type Object
4642          */
4643         OKCANCEL : [
4644             {
4645                name : 'cancel',
4646                 html : 'Cancel'
4647             },
4648             {
4649                 name : 'ok',
4650                 weight : 'primary',
4651                 html : 'OK'
4652             }
4653         ],
4654         /**
4655          * Button config that displays Yes, No and Cancel buttons
4656          * @type Object
4657          */
4658         YESNOCANCEL : [
4659             {
4660                 name : 'yes',
4661                 weight : 'primary',
4662                 html : 'Yes'
4663             },
4664             {
4665                 name : 'no',
4666                 html : 'No'
4667             },
4668             {
4669                 name : 'cancel',
4670                 html : 'Cancel'
4671             }
4672         ],
4673         
4674         zIndex : 10001
4675 });
4676
4677 /*
4678  * - LGPL
4679  *
4680  * messagebox - can be used as a replace
4681  * 
4682  */
4683 /**
4684  * @class Roo.MessageBox
4685  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4686  * Example usage:
4687  *<pre><code>
4688 // Basic alert:
4689 Roo.Msg.alert('Status', 'Changes saved successfully.');
4690
4691 // Prompt for user data:
4692 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4693     if (btn == 'ok'){
4694         // process text value...
4695     }
4696 });
4697
4698 // Show a dialog using config options:
4699 Roo.Msg.show({
4700    title:'Save Changes?',
4701    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4702    buttons: Roo.Msg.YESNOCANCEL,
4703    fn: processResult,
4704    animEl: 'elId'
4705 });
4706 </code></pre>
4707  * @singleton
4708  */
4709 Roo.bootstrap.MessageBox = function(){
4710     var dlg, opt, mask, waitTimer;
4711     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4712     var buttons, activeTextEl, bwidth;
4713
4714     
4715     // private
4716     var handleButton = function(button){
4717         dlg.hide();
4718         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4719     };
4720
4721     // private
4722     var handleHide = function(){
4723         if(opt && opt.cls){
4724             dlg.el.removeClass(opt.cls);
4725         }
4726         //if(waitTimer){
4727         //    Roo.TaskMgr.stop(waitTimer);
4728         //    waitTimer = null;
4729         //}
4730     };
4731
4732     // private
4733     var updateButtons = function(b){
4734         var width = 0;
4735         if(!b){
4736             buttons["ok"].hide();
4737             buttons["cancel"].hide();
4738             buttons["yes"].hide();
4739             buttons["no"].hide();
4740             dlg.footerEl.hide();
4741             
4742             return width;
4743         }
4744         dlg.footerEl.show();
4745         for(var k in buttons){
4746             if(typeof buttons[k] != "function"){
4747                 if(b[k]){
4748                     buttons[k].show();
4749                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4750                     width += buttons[k].el.getWidth()+15;
4751                 }else{
4752                     buttons[k].hide();
4753                 }
4754             }
4755         }
4756         return width;
4757     };
4758
4759     // private
4760     var handleEsc = function(d, k, e){
4761         if(opt && opt.closable !== false){
4762             dlg.hide();
4763         }
4764         if(e){
4765             e.stopEvent();
4766         }
4767     };
4768
4769     return {
4770         /**
4771          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4772          * @return {Roo.BasicDialog} The BasicDialog element
4773          */
4774         getDialog : function(){
4775            if(!dlg){
4776                 dlg = new Roo.bootstrap.Modal( {
4777                     //draggable: true,
4778                     //resizable:false,
4779                     //constraintoviewport:false,
4780                     //fixedcenter:true,
4781                     //collapsible : false,
4782                     //shim:true,
4783                     //modal: true,
4784                 //    width: 'auto',
4785                   //  height:100,
4786                     //buttonAlign:"center",
4787                     closeClick : function(){
4788                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4789                             handleButton("no");
4790                         }else{
4791                             handleButton("cancel");
4792                         }
4793                     }
4794                 });
4795                 dlg.render();
4796                 dlg.on("hide", handleHide);
4797                 mask = dlg.mask;
4798                 //dlg.addKeyListener(27, handleEsc);
4799                 buttons = {};
4800                 this.buttons = buttons;
4801                 var bt = this.buttonText;
4802                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4803                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4804                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4805                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4806                 //Roo.log(buttons);
4807                 bodyEl = dlg.bodyEl.createChild({
4808
4809                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4810                         '<textarea class="roo-mb-textarea"></textarea>' +
4811                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4812                 });
4813                 msgEl = bodyEl.dom.firstChild;
4814                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4815                 textboxEl.enableDisplayMode();
4816                 textboxEl.addKeyListener([10,13], function(){
4817                     if(dlg.isVisible() && opt && opt.buttons){
4818                         if(opt.buttons.ok){
4819                             handleButton("ok");
4820                         }else if(opt.buttons.yes){
4821                             handleButton("yes");
4822                         }
4823                     }
4824                 });
4825                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4826                 textareaEl.enableDisplayMode();
4827                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4828                 progressEl.enableDisplayMode();
4829                 
4830                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4831                 var pf = progressEl.dom.firstChild;
4832                 if (pf) {
4833                     pp = Roo.get(pf.firstChild);
4834                     pp.setHeight(pf.offsetHeight);
4835                 }
4836                 
4837             }
4838             return dlg;
4839         },
4840
4841         /**
4842          * Updates the message box body text
4843          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4844          * the XHTML-compliant non-breaking space character '&amp;#160;')
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateText : function(text)
4848         {
4849             if(!dlg.isVisible() && !opt.width){
4850                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4851                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4852             }
4853             msgEl.innerHTML = text || '&#160;';
4854       
4855             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4856             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4857             var w = Math.max(
4858                     Math.min(opt.width || cw , this.maxWidth), 
4859                     Math.max(opt.minWidth || this.minWidth, bwidth)
4860             );
4861             if(opt.prompt){
4862                 activeTextEl.setWidth(w);
4863             }
4864             if(dlg.isVisible()){
4865                 dlg.fixedcenter = false;
4866             }
4867             // to big, make it scroll. = But as usual stupid IE does not support
4868             // !important..
4869             
4870             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4871                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4872                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4873             } else {
4874                 bodyEl.dom.style.height = '';
4875                 bodyEl.dom.style.overflowY = '';
4876             }
4877             if (cw > w) {
4878                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4879             } else {
4880                 bodyEl.dom.style.overflowX = '';
4881             }
4882             
4883             dlg.setContentSize(w, bodyEl.getHeight());
4884             if(dlg.isVisible()){
4885                 dlg.fixedcenter = true;
4886             }
4887             return this;
4888         },
4889
4890         /**
4891          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4892          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4893          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4894          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4895          * @return {Roo.MessageBox} This message box
4896          */
4897         updateProgress : function(value, text){
4898             if(text){
4899                 this.updateText(text);
4900             }
4901             
4902             if (pp) { // weird bug on my firefox - for some reason this is not defined
4903                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4904                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4905             }
4906             return this;
4907         },        
4908
4909         /**
4910          * Returns true if the message box is currently displayed
4911          * @return {Boolean} True if the message box is visible, else false
4912          */
4913         isVisible : function(){
4914             return dlg && dlg.isVisible();  
4915         },
4916
4917         /**
4918          * Hides the message box if it is displayed
4919          */
4920         hide : function(){
4921             if(this.isVisible()){
4922                 dlg.hide();
4923             }  
4924         },
4925
4926         /**
4927          * Displays a new message box, or reinitializes an existing message box, based on the config options
4928          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4929          * The following config object properties are supported:
4930          * <pre>
4931 Property    Type             Description
4932 ----------  ---------------  ------------------------------------------------------------------------------------
4933 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4934                                    closes (defaults to undefined)
4935 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4936                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4937 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4938                                    progress and wait dialogs will ignore this property and always hide the
4939                                    close button as they can only be closed programmatically.
4940 cls               String           A custom CSS class to apply to the message box element
4941 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4942                                    displayed (defaults to 75)
4943 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4944                                    function will be btn (the name of the button that was clicked, if applicable,
4945                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4946                                    Progress and wait dialogs will ignore this option since they do not respond to
4947                                    user actions and can only be closed programmatically, so any required function
4948                                    should be called by the same code after it closes the dialog.
4949 icon              String           A CSS class that provides a background image to be used as an icon for
4950                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4951 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4952 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4953 modal             Boolean          False to allow user interaction with the page while the message box is
4954                                    displayed (defaults to true)
4955 msg               String           A string that will replace the existing message box body text (defaults
4956                                    to the XHTML-compliant non-breaking space character '&#160;')
4957 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4958 progress          Boolean          True to display a progress bar (defaults to false)
4959 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4960 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4961 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4962 title             String           The title text
4963 value             String           The string value to set into the active textbox element if displayed
4964 wait              Boolean          True to display a progress bar (defaults to false)
4965 width             Number           The width of the dialog in pixels
4966 </pre>
4967          *
4968          * Example usage:
4969          * <pre><code>
4970 Roo.Msg.show({
4971    title: 'Address',
4972    msg: 'Please enter your address:',
4973    width: 300,
4974    buttons: Roo.MessageBox.OKCANCEL,
4975    multiline: true,
4976    fn: saveAddress,
4977    animEl: 'addAddressBtn'
4978 });
4979 </code></pre>
4980          * @param {Object} config Configuration options
4981          * @return {Roo.MessageBox} This message box
4982          */
4983         show : function(options)
4984         {
4985             
4986             // this causes nightmares if you show one dialog after another
4987             // especially on callbacks..
4988              
4989             if(this.isVisible()){
4990                 
4991                 this.hide();
4992                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4993                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4994                 Roo.log("New Dialog Message:" +  options.msg )
4995                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4996                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4997                 
4998             }
4999             var d = this.getDialog();
5000             opt = options;
5001             d.setTitle(opt.title || "&#160;");
5002             d.closeEl.setDisplayed(opt.closable !== false);
5003             activeTextEl = textboxEl;
5004             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5005             if(opt.prompt){
5006                 if(opt.multiline){
5007                     textboxEl.hide();
5008                     textareaEl.show();
5009                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5010                         opt.multiline : this.defaultTextHeight);
5011                     activeTextEl = textareaEl;
5012                 }else{
5013                     textboxEl.show();
5014                     textareaEl.hide();
5015                 }
5016             }else{
5017                 textboxEl.hide();
5018                 textareaEl.hide();
5019             }
5020             progressEl.setDisplayed(opt.progress === true);
5021             if (opt.progress) {
5022                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5023             }
5024             this.updateProgress(0);
5025             activeTextEl.dom.value = opt.value || "";
5026             if(opt.prompt){
5027                 dlg.setDefaultButton(activeTextEl);
5028             }else{
5029                 var bs = opt.buttons;
5030                 var db = null;
5031                 if(bs && bs.ok){
5032                     db = buttons["ok"];
5033                 }else if(bs && bs.yes){
5034                     db = buttons["yes"];
5035                 }
5036                 dlg.setDefaultButton(db);
5037             }
5038             bwidth = updateButtons(opt.buttons);
5039             this.updateText(opt.msg);
5040             if(opt.cls){
5041                 d.el.addClass(opt.cls);
5042             }
5043             d.proxyDrag = opt.proxyDrag === true;
5044             d.modal = opt.modal !== false;
5045             d.mask = opt.modal !== false ? mask : false;
5046             if(!d.isVisible()){
5047                 // force it to the end of the z-index stack so it gets a cursor in FF
5048                 document.body.appendChild(dlg.el.dom);
5049                 d.animateTarget = null;
5050                 d.show(options.animEl);
5051             }
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5057          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5058          * and closing the message box when the process is complete.
5059          * @param {String} title The title bar text
5060          * @param {String} msg The message box body text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         progress : function(title, msg){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 progress:true,
5069                 closable:false,
5070                 minWidth: this.minProgressWidth,
5071                 modal : true
5072             });
5073             return this;
5074         },
5075
5076         /**
5077          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5078          * If a callback function is passed it will be called after the user clicks the button, and the
5079          * id of the button that was clicked will be passed as the only parameter to the callback
5080          * (could also be the top-right close button).
5081          * @param {String} title The title bar text
5082          * @param {String} msg The message box body text
5083          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5084          * @param {Object} scope (optional) The scope of the callback function
5085          * @return {Roo.MessageBox} This message box
5086          */
5087         alert : function(title, msg, fn, scope)
5088         {
5089             this.show({
5090                 title : title,
5091                 msg : msg,
5092                 buttons: this.OK,
5093                 fn: fn,
5094                 closable : false,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5103          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5104          * You are responsible for closing the message box when the process is complete.
5105          * @param {String} msg The message box body text
5106          * @param {String} title (optional) The title bar text
5107          * @return {Roo.MessageBox} This message box
5108          */
5109         wait : function(msg, title){
5110             this.show({
5111                 title : title,
5112                 msg : msg,
5113                 buttons: false,
5114                 closable:false,
5115                 progress:true,
5116                 modal:true,
5117                 width:300,
5118                 wait:true
5119             });
5120             waitTimer = Roo.TaskMgr.start({
5121                 run: function(i){
5122                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5123                 },
5124                 interval: 1000
5125             });
5126             return this;
5127         },
5128
5129         /**
5130          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5131          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5132          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5133          * @param {String} title The title bar text
5134          * @param {String} msg The message box body text
5135          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5136          * @param {Object} scope (optional) The scope of the callback function
5137          * @return {Roo.MessageBox} This message box
5138          */
5139         confirm : function(title, msg, fn, scope){
5140             this.show({
5141                 title : title,
5142                 msg : msg,
5143                 buttons: this.YESNO,
5144                 fn: fn,
5145                 scope : scope,
5146                 modal : true
5147             });
5148             return this;
5149         },
5150
5151         /**
5152          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5153          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5154          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5155          * (could also be the top-right close button) and the text that was entered will be passed as the two
5156          * parameters to the callback.
5157          * @param {String} title The title bar text
5158          * @param {String} msg The message box body text
5159          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5160          * @param {Object} scope (optional) The scope of the callback function
5161          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5162          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5163          * @return {Roo.MessageBox} This message box
5164          */
5165         prompt : function(title, msg, fn, scope, multiline){
5166             this.show({
5167                 title : title,
5168                 msg : msg,
5169                 buttons: this.OKCANCEL,
5170                 fn: fn,
5171                 minWidth:250,
5172                 scope : scope,
5173                 prompt:true,
5174                 multiline: multiline,
5175                 modal : true
5176             });
5177             return this;
5178         },
5179
5180         /**
5181          * Button config that displays a single OK button
5182          * @type Object
5183          */
5184         OK : {ok:true},
5185         /**
5186          * Button config that displays Yes and No buttons
5187          * @type Object
5188          */
5189         YESNO : {yes:true, no:true},
5190         /**
5191          * Button config that displays OK and Cancel buttons
5192          * @type Object
5193          */
5194         OKCANCEL : {ok:true, cancel:true},
5195         /**
5196          * Button config that displays Yes, No and Cancel buttons
5197          * @type Object
5198          */
5199         YESNOCANCEL : {yes:true, no:true, cancel:true},
5200
5201         /**
5202          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5203          * @type Number
5204          */
5205         defaultTextHeight : 75,
5206         /**
5207          * The maximum width in pixels of the message box (defaults to 600)
5208          * @type Number
5209          */
5210         maxWidth : 600,
5211         /**
5212          * The minimum width in pixels of the message box (defaults to 100)
5213          * @type Number
5214          */
5215         minWidth : 100,
5216         /**
5217          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5218          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5219          * @type Number
5220          */
5221         minProgressWidth : 250,
5222         /**
5223          * An object containing the default button text strings that can be overriden for localized language support.
5224          * Supported properties are: ok, cancel, yes and no.
5225          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5226          * @type Object
5227          */
5228         buttonText : {
5229             ok : "OK",
5230             cancel : "Cancel",
5231             yes : "Yes",
5232             no : "No"
5233         }
5234     };
5235 }();
5236
5237 /**
5238  * Shorthand for {@link Roo.MessageBox}
5239  */
5240 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5241 Roo.Msg = Roo.Msg || Roo.MessageBox;
5242 /*
5243  * - LGPL
5244  *
5245  * navbar
5246  * 
5247  */
5248
5249 /**
5250  * @class Roo.bootstrap.Navbar
5251  * @extends Roo.bootstrap.Component
5252  * Bootstrap Navbar class
5253
5254  * @constructor
5255  * Create a new Navbar
5256  * @param {Object} config The config object
5257  */
5258
5259
5260 Roo.bootstrap.Navbar = function(config){
5261     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5262     this.addEvents({
5263         // raw events
5264         /**
5265          * @event beforetoggle
5266          * Fire before toggle the menu
5267          * @param {Roo.EventObject} e
5268          */
5269         "beforetoggle" : true
5270     });
5271 };
5272
5273 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5274     
5275     
5276    
5277     // private
5278     navItems : false,
5279     loadMask : false,
5280     
5281     
5282     getAutoCreate : function(){
5283         
5284         
5285         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5286         
5287     },
5288     
5289     initEvents :function ()
5290     {
5291         //Roo.log(this.el.select('.navbar-toggle',true));
5292         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5293         
5294         var mark = {
5295             tag: "div",
5296             cls:"x-dlg-mask"
5297         };
5298         
5299         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5300         
5301         var size = this.el.getSize();
5302         this.maskEl.setSize(size.width, size.height);
5303         this.maskEl.enableDisplayMode("block");
5304         this.maskEl.hide();
5305         
5306         if(this.loadMask){
5307             this.maskEl.show();
5308         }
5309     },
5310     
5311     
5312     getChildContainer : function()
5313     {
5314         if (this.el && this.el.select('.collapse').getCount()) {
5315             return this.el.select('.collapse',true).first();
5316         }
5317         
5318         return this.el;
5319     },
5320     
5321     mask : function()
5322     {
5323         this.maskEl.show();
5324     },
5325     
5326     unmask : function()
5327     {
5328         this.maskEl.hide();
5329     },
5330     onToggle : function()
5331     {
5332         
5333         if(this.fireEvent('beforetoggle', this) === false){
5334             return;
5335         }
5336         var ce = this.el.select('.navbar-collapse',true).first();
5337       
5338         if (!ce.hasClass('show')) {
5339            this.expand();
5340         } else {
5341             this.collapse();
5342         }
5343         
5344         
5345     
5346     },
5347     /**
5348      * Expand the navbar pulldown 
5349      */
5350     expand : function ()
5351     {
5352        
5353         var ce = this.el.select('.navbar-collapse',true).first();
5354         if (ce.hasClass('collapsing')) {
5355             return;
5356         }
5357         ce.dom.style.height = '';
5358                // show it...
5359         ce.addClass('in'); // old...
5360         ce.removeClass('collapse');
5361         ce.addClass('show');
5362         var h = ce.getHeight();
5363         Roo.log(h);
5364         ce.removeClass('show');
5365         // at this point we should be able to see it..
5366         ce.addClass('collapsing');
5367         
5368         ce.setHeight(0); // resize it ...
5369         ce.on('transitionend', function() {
5370             //Roo.log('done transition');
5371             ce.removeClass('collapsing');
5372             ce.addClass('show');
5373             ce.removeClass('collapse');
5374
5375             ce.dom.style.height = '';
5376         }, this, { single: true} );
5377         ce.setHeight(h);
5378         ce.dom.scrollTop = 0;
5379     },
5380     /**
5381      * Collapse the navbar pulldown 
5382      */
5383     collapse : function()
5384     {
5385          var ce = this.el.select('.navbar-collapse',true).first();
5386        
5387         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5388             // it's collapsed or collapsing..
5389             return;
5390         }
5391         ce.removeClass('in'); // old...
5392         ce.setHeight(ce.getHeight());
5393         ce.removeClass('show');
5394         ce.addClass('collapsing');
5395         
5396         ce.on('transitionend', function() {
5397             ce.dom.style.height = '';
5398             ce.removeClass('collapsing');
5399             ce.addClass('collapse');
5400         }, this, { single: true} );
5401         ce.setHeight(0);
5402     }
5403     
5404     
5405     
5406 });
5407
5408
5409
5410  
5411
5412  /*
5413  * - LGPL
5414  *
5415  * navbar
5416  * 
5417  */
5418
5419 /**
5420  * @class Roo.bootstrap.NavSimplebar
5421  * @extends Roo.bootstrap.Navbar
5422  * Bootstrap Sidebar class
5423  *
5424  * @cfg {Boolean} inverse is inverted color
5425  * 
5426  * @cfg {String} type (nav | pills | tabs)
5427  * @cfg {Boolean} arrangement stacked | justified
5428  * @cfg {String} align (left | right) alignment
5429  * 
5430  * @cfg {Boolean} main (true|false) main nav bar? default false
5431  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5432  * 
5433  * @cfg {String} tag (header|footer|nav|div) default is nav 
5434
5435  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5436  * 
5437  * 
5438  * @constructor
5439  * Create a new Sidebar
5440  * @param {Object} config The config object
5441  */
5442
5443
5444 Roo.bootstrap.NavSimplebar = function(config){
5445     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5449     
5450     inverse: false,
5451     
5452     type: false,
5453     arrangement: '',
5454     align : false,
5455     
5456     weight : 'light',
5457     
5458     main : false,
5459     
5460     
5461     tag : false,
5462     
5463     
5464     getAutoCreate : function(){
5465         
5466         
5467         var cfg = {
5468             tag : this.tag || 'div',
5469             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5470         };
5471         if (['light','white'].indexOf(this.weight) > -1) {
5472             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5473         }
5474         cfg.cls += ' bg-' + this.weight;
5475         
5476         if (this.inverse) {
5477             cfg.cls += ' navbar-inverse';
5478             
5479         }
5480         
5481         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5482         
5483         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5484             return cfg;
5485         }
5486         
5487         
5488     
5489         
5490         cfg.cn = [
5491             {
5492                 cls: 'nav nav-' + this.xtype,
5493                 tag : 'ul'
5494             }
5495         ];
5496         
5497          
5498         this.type = this.type || 'nav';
5499         if (['tabs','pills'].indexOf(this.type) != -1) {
5500             cfg.cn[0].cls += ' nav-' + this.type
5501         
5502         
5503         } else {
5504             if (this.type!=='nav') {
5505                 Roo.log('nav type must be nav/tabs/pills')
5506             }
5507             cfg.cn[0].cls += ' navbar-nav'
5508         }
5509         
5510         
5511         
5512         
5513         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5514             cfg.cn[0].cls += ' nav-' + this.arrangement;
5515         }
5516         
5517         
5518         if (this.align === 'right') {
5519             cfg.cn[0].cls += ' navbar-right';
5520         }
5521         
5522         
5523         
5524         
5525         return cfg;
5526     
5527         
5528     }
5529     
5530     
5531     
5532 });
5533
5534
5535
5536  
5537
5538  
5539        /*
5540  * - LGPL
5541  *
5542  * navbar
5543  * navbar-fixed-top
5544  * navbar-expand-md  fixed-top 
5545  */
5546
5547 /**
5548  * @class Roo.bootstrap.NavHeaderbar
5549  * @extends Roo.bootstrap.NavSimplebar
5550  * Bootstrap Sidebar class
5551  *
5552  * @cfg {String} brand what is brand
5553  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5554  * @cfg {String} brand_href href of the brand
5555  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5556  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5557  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5558  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5559  * 
5560  * @constructor
5561  * Create a new Sidebar
5562  * @param {Object} config The config object
5563  */
5564
5565
5566 Roo.bootstrap.NavHeaderbar = function(config){
5567     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5568       
5569 };
5570
5571 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5572     
5573     position: '',
5574     brand: '',
5575     brand_href: false,
5576     srButton : true,
5577     autohide : false,
5578     desktopCenter : false,
5579    
5580     
5581     getAutoCreate : function(){
5582         
5583         var   cfg = {
5584             tag: this.nav || 'nav',
5585             cls: 'navbar navbar-expand-md',
5586             role: 'navigation',
5587             cn: []
5588         };
5589         
5590         var cn = cfg.cn;
5591         if (this.desktopCenter) {
5592             cn.push({cls : 'container', cn : []});
5593             cn = cn[0].cn;
5594         }
5595         
5596         if(this.srButton){
5597             var btn = {
5598                 tag: 'button',
5599                 type: 'button',
5600                 cls: 'navbar-toggle navbar-toggler',
5601                 'data-toggle': 'collapse',
5602                 cn: [
5603                     {
5604                         tag: 'span',
5605                         cls: 'sr-only',
5606                         html: 'Toggle navigation'
5607                     },
5608                     {
5609                         tag: 'span',
5610                         cls: 'icon-bar navbar-toggler-icon'
5611                     },
5612                     {
5613                         tag: 'span',
5614                         cls: 'icon-bar'
5615                     },
5616                     {
5617                         tag: 'span',
5618                         cls: 'icon-bar'
5619                     }
5620                 ]
5621             };
5622             
5623             cn.push( Roo.bootstrap.version == 4 ? btn : {
5624                 tag: 'div',
5625                 cls: 'navbar-header',
5626                 cn: [
5627                     btn
5628                 ]
5629             });
5630         }
5631         
5632         cn.push({
5633             tag: 'div',
5634             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5635             cn : []
5636         });
5637         
5638         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5639         
5640         if (['light','white'].indexOf(this.weight) > -1) {
5641             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5642         }
5643         cfg.cls += ' bg-' + this.weight;
5644         
5645         
5646         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5647             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5648             
5649             // tag can override this..
5650             
5651             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5652         }
5653         
5654         if (this.brand !== '') {
5655             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5656             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5657                 tag: 'a',
5658                 href: this.brand_href ? this.brand_href : '#',
5659                 cls: 'navbar-brand',
5660                 cn: [
5661                 this.brand
5662                 ]
5663             });
5664         }
5665         
5666         if(this.main){
5667             cfg.cls += ' main-nav';
5668         }
5669         
5670         
5671         return cfg;
5672
5673         
5674     },
5675     getHeaderChildContainer : function()
5676     {
5677         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5678             return this.el.select('.navbar-header',true).first();
5679         }
5680         
5681         return this.getChildContainer();
5682     },
5683     
5684     getChildContainer : function()
5685     {
5686          
5687         return this.el.select('.roo-navbar-collapse',true).first();
5688          
5689         
5690     },
5691     
5692     initEvents : function()
5693     {
5694         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5695         
5696         if (this.autohide) {
5697             
5698             var prevScroll = 0;
5699             var ft = this.el;
5700             
5701             Roo.get(document).on('scroll',function(e) {
5702                 var ns = Roo.get(document).getScroll().top;
5703                 var os = prevScroll;
5704                 prevScroll = ns;
5705                 
5706                 if(ns > os){
5707                     ft.removeClass('slideDown');
5708                     ft.addClass('slideUp');
5709                     return;
5710                 }
5711                 ft.removeClass('slideUp');
5712                 ft.addClass('slideDown');
5713                  
5714               
5715           },this);
5716         }
5717     }    
5718     
5719 });
5720
5721
5722
5723  
5724
5725  /*
5726  * - LGPL
5727  *
5728  * navbar
5729  * 
5730  */
5731
5732 /**
5733  * @class Roo.bootstrap.NavSidebar
5734  * @extends Roo.bootstrap.Navbar
5735  * Bootstrap Sidebar class
5736  * 
5737  * @constructor
5738  * Create a new Sidebar
5739  * @param {Object} config The config object
5740  */
5741
5742
5743 Roo.bootstrap.NavSidebar = function(config){
5744     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5745 };
5746
5747 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5748     
5749     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5750     
5751     getAutoCreate : function(){
5752         
5753         
5754         return  {
5755             tag: 'div',
5756             cls: 'sidebar sidebar-nav'
5757         };
5758     
5759         
5760     }
5761     
5762     
5763     
5764 });
5765
5766
5767
5768  
5769
5770  /*
5771  * - LGPL
5772  *
5773  * nav group
5774  * 
5775  */
5776
5777 /**
5778  * @class Roo.bootstrap.NavGroup
5779  * @extends Roo.bootstrap.Component
5780  * Bootstrap NavGroup class
5781  * @cfg {String} align (left|right)
5782  * @cfg {Boolean} inverse
5783  * @cfg {String} type (nav|pills|tab) default nav
5784  * @cfg {String} navId - reference Id for navbar.
5785  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5786  * 
5787  * @constructor
5788  * Create a new nav group
5789  * @param {Object} config The config object
5790  */
5791
5792 Roo.bootstrap.NavGroup = function(config){
5793     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5794     this.navItems = [];
5795    
5796     Roo.bootstrap.NavGroup.register(this);
5797      this.addEvents({
5798         /**
5799              * @event changed
5800              * Fires when the active item changes
5801              * @param {Roo.bootstrap.NavGroup} this
5802              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5803              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5804          */
5805         'changed': true
5806      });
5807     
5808 };
5809
5810 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5811     
5812     align: '',
5813     inverse: false,
5814     form: false,
5815     type: 'nav',
5816     navId : '',
5817     // private
5818     pilltype : true,
5819     
5820     navItems : false, 
5821     
5822     getAutoCreate : function()
5823     {
5824         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5825         
5826         cfg = {
5827             tag : 'ul',
5828             cls: 'nav' 
5829         };
5830         if (Roo.bootstrap.version == 4) {
5831             if (['tabs','pills'].indexOf(this.type) != -1) {
5832                 cfg.cls += ' nav-' + this.type; 
5833             } else {
5834                 // trying to remove so header bar can right align top?
5835                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5836                     // do not use on header bar... 
5837                     cfg.cls += ' navbar-nav';
5838                 }
5839             }
5840             
5841         } else {
5842             if (['tabs','pills'].indexOf(this.type) != -1) {
5843                 cfg.cls += ' nav-' + this.type
5844             } else {
5845                 if (this.type !== 'nav') {
5846                     Roo.log('nav type must be nav/tabs/pills')
5847                 }
5848                 cfg.cls += ' navbar-nav'
5849             }
5850         }
5851         
5852         if (this.parent() && this.parent().sidebar) {
5853             cfg = {
5854                 tag: 'ul',
5855                 cls: 'dashboard-menu sidebar-menu'
5856             };
5857             
5858             return cfg;
5859         }
5860         
5861         if (this.form === true) {
5862             cfg = {
5863                 tag: 'form',
5864                 cls: 'navbar-form form-inline'
5865             };
5866             //nav navbar-right ml-md-auto
5867             if (this.align === 'right') {
5868                 cfg.cls += ' navbar-right ml-md-auto';
5869             } else {
5870                 cfg.cls += ' navbar-left';
5871             }
5872         }
5873         
5874         if (this.align === 'right') {
5875             cfg.cls += ' navbar-right ml-md-auto';
5876         } else {
5877             cfg.cls += ' mr-auto';
5878         }
5879         
5880         if (this.inverse) {
5881             cfg.cls += ' navbar-inverse';
5882             
5883         }
5884         
5885         
5886         return cfg;
5887     },
5888     /**
5889     * sets the active Navigation item
5890     * @param {Roo.bootstrap.NavItem} the new current navitem
5891     */
5892     setActiveItem : function(item)
5893     {
5894         var prev = false;
5895         Roo.each(this.navItems, function(v){
5896             if (v == item) {
5897                 return ;
5898             }
5899             if (v.isActive()) {
5900                 v.setActive(false, true);
5901                 prev = v;
5902                 
5903             }
5904             
5905         });
5906
5907         item.setActive(true, true);
5908         this.fireEvent('changed', this, item, prev);
5909         
5910         
5911     },
5912     /**
5913     * gets the active Navigation item
5914     * @return {Roo.bootstrap.NavItem} the current navitem
5915     */
5916     getActive : function()
5917     {
5918         
5919         var prev = false;
5920         Roo.each(this.navItems, function(v){
5921             
5922             if (v.isActive()) {
5923                 prev = v;
5924                 
5925             }
5926             
5927         });
5928         return prev;
5929     },
5930     
5931     indexOfNav : function()
5932     {
5933         
5934         var prev = false;
5935         Roo.each(this.navItems, function(v,i){
5936             
5937             if (v.isActive()) {
5938                 prev = i;
5939                 
5940             }
5941             
5942         });
5943         return prev;
5944     },
5945     /**
5946     * adds a Navigation item
5947     * @param {Roo.bootstrap.NavItem} the navitem to add
5948     */
5949     addItem : function(cfg)
5950     {
5951         if (this.form && Roo.bootstrap.version == 4) {
5952             cfg.tag = 'div';
5953         }
5954         var cn = new Roo.bootstrap.NavItem(cfg);
5955         this.register(cn);
5956         cn.parentId = this.id;
5957         cn.onRender(this.el, null);
5958         return cn;
5959     },
5960     /**
5961     * register a Navigation item
5962     * @param {Roo.bootstrap.NavItem} the navitem to add
5963     */
5964     register : function(item)
5965     {
5966         this.navItems.push( item);
5967         item.navId = this.navId;
5968     
5969     },
5970     
5971     /**
5972     * clear all the Navigation item
5973     */
5974    
5975     clearAll : function()
5976     {
5977         this.navItems = [];
5978         this.el.dom.innerHTML = '';
5979     },
5980     
5981     getNavItem: function(tabId)
5982     {
5983         var ret = false;
5984         Roo.each(this.navItems, function(e) {
5985             if (e.tabId == tabId) {
5986                ret =  e;
5987                return false;
5988             }
5989             return true;
5990             
5991         });
5992         return ret;
5993     },
5994     
5995     setActiveNext : function()
5996     {
5997         var i = this.indexOfNav(this.getActive());
5998         if (i > this.navItems.length) {
5999             return;
6000         }
6001         this.setActiveItem(this.navItems[i+1]);
6002     },
6003     setActivePrev : function()
6004     {
6005         var i = this.indexOfNav(this.getActive());
6006         if (i  < 1) {
6007             return;
6008         }
6009         this.setActiveItem(this.navItems[i-1]);
6010     },
6011     clearWasActive : function(except) {
6012         Roo.each(this.navItems, function(e) {
6013             if (e.tabId != except.tabId && e.was_active) {
6014                e.was_active = false;
6015                return false;
6016             }
6017             return true;
6018             
6019         });
6020     },
6021     getWasActive : function ()
6022     {
6023         var r = false;
6024         Roo.each(this.navItems, function(e) {
6025             if (e.was_active) {
6026                r = e;
6027                return false;
6028             }
6029             return true;
6030             
6031         });
6032         return r;
6033     }
6034     
6035     
6036 });
6037
6038  
6039 Roo.apply(Roo.bootstrap.NavGroup, {
6040     
6041     groups: {},
6042      /**
6043     * register a Navigation Group
6044     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6045     */
6046     register : function(navgrp)
6047     {
6048         this.groups[navgrp.navId] = navgrp;
6049         
6050     },
6051     /**
6052     * fetch a Navigation Group based on the navigation ID
6053     * @param {string} the navgroup to add
6054     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6055     */
6056     get: function(navId) {
6057         if (typeof(this.groups[navId]) == 'undefined') {
6058             return false;
6059             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6060         }
6061         return this.groups[navId] ;
6062     }
6063     
6064     
6065     
6066 });
6067
6068  /*
6069  * - LGPL
6070  *
6071  * row
6072  * 
6073  */
6074
6075 /**
6076  * @class Roo.bootstrap.NavItem
6077  * @extends Roo.bootstrap.Component
6078  * Bootstrap Navbar.NavItem class
6079  * @cfg {String} href  link to
6080  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6081  * @cfg {Boolean} button_outline show and outlined button
6082  * @cfg {String} html content of button
6083  * @cfg {String} badge text inside badge
6084  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6085  * @cfg {String} glyphicon DEPRICATED - use fa
6086  * @cfg {String} icon DEPRICATED - use fa
6087  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6088  * @cfg {Boolean} active Is item active
6089  * @cfg {Boolean} disabled Is item disabled
6090  * @cfg {String} linkcls  Link Class
6091  * @cfg {Boolean} preventDefault (true | false) default false
6092  * @cfg {String} tabId the tab that this item activates.
6093  * @cfg {String} tagtype (a|span) render as a href or span?
6094  * @cfg {Boolean} animateRef (true|false) link to element default false  
6095   
6096  * @constructor
6097  * Create a new Navbar Item
6098  * @param {Object} config The config object
6099  */
6100 Roo.bootstrap.NavItem = function(config){
6101     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6102     this.addEvents({
6103         // raw events
6104         /**
6105          * @event click
6106          * The raw click event for the entire grid.
6107          * @param {Roo.EventObject} e
6108          */
6109         "click" : true,
6110          /**
6111             * @event changed
6112             * Fires when the active item active state changes
6113             * @param {Roo.bootstrap.NavItem} this
6114             * @param {boolean} state the new state
6115              
6116          */
6117         'changed': true,
6118         /**
6119             * @event scrollto
6120             * Fires when scroll to element
6121             * @param {Roo.bootstrap.NavItem} this
6122             * @param {Object} options
6123             * @param {Roo.EventObject} e
6124              
6125          */
6126         'scrollto': true
6127     });
6128    
6129 };
6130
6131 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6132     
6133     href: false,
6134     html: '',
6135     badge: '',
6136     icon: false,
6137     fa : false,
6138     glyphicon: false,
6139     active: false,
6140     preventDefault : false,
6141     tabId : false,
6142     tagtype : 'a',
6143     tag: 'li',
6144     disabled : false,
6145     animateRef : false,
6146     was_active : false,
6147     button_weight : '',
6148     button_outline : false,
6149     linkcls : '',
6150     navLink: false,
6151     
6152     getAutoCreate : function(){
6153          
6154         var cfg = {
6155             tag: this.tag,
6156             cls: 'nav-item'
6157         };
6158         
6159         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6160         
6161         if (this.active) {
6162             cfg.cls +=  ' active' ;
6163         }
6164         if (this.disabled) {
6165             cfg.cls += ' disabled';
6166         }
6167         
6168         // BS4 only?
6169         if (this.button_weight.length) {
6170             cfg.tag = this.href ? 'a' : 'button';
6171             cfg.html = this.html || '';
6172             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6173             if (this.href) {
6174                 cfg.href = this.href;
6175             }
6176             if (this.fa) {
6177                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6178             }
6179             
6180             // menu .. should add dropdown-menu class - so no need for carat..
6181             
6182             if (this.badge !== '') {
6183                  
6184                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6185             }
6186             return cfg;
6187         }
6188         
6189         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6190             cfg.cn = [
6191                 {
6192                     tag: this.tagtype,
6193                     href : this.href || "#",
6194                     html: this.html || ''
6195                 }
6196             ];
6197             if (this.tagtype == 'a') {
6198                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6199         
6200             }
6201             if (this.icon) {
6202                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6203             }
6204             if (this.fa) {
6205                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6206             }
6207             if(this.glyphicon) {
6208                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6209             }
6210             
6211             if (this.menu) {
6212                 
6213                 cfg.cn[0].html += " <span class='caret'></span>";
6214              
6215             }
6216             
6217             if (this.badge !== '') {
6218                  
6219                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6220             }
6221         }
6222         
6223         
6224         
6225         return cfg;
6226     },
6227     onRender : function(ct, position)
6228     {
6229        // Roo.log("Call onRender: " + this.xtype);
6230         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6231             this.tag = 'div';
6232         }
6233         
6234         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6235         this.navLink = this.el.select('.nav-link',true).first();
6236         return ret;
6237     },
6238       
6239     
6240     initEvents: function() 
6241     {
6242         if (typeof (this.menu) != 'undefined') {
6243             this.menu.parentType = this.xtype;
6244             this.menu.triggerEl = this.el;
6245             this.menu = this.addxtype(Roo.apply({}, this.menu));
6246         }
6247         
6248         this.el.on('click', this.onClick, this);
6249         
6250         //if(this.tagtype == 'span'){
6251         //    this.el.select('span',true).on('click', this.onClick, this);
6252         //}
6253        
6254         // at this point parent should be available..
6255         this.parent().register(this);
6256     },
6257     
6258     onClick : function(e)
6259     {
6260         if (e.getTarget('.dropdown-menu-item')) {
6261             // did you click on a menu itemm.... - then don't trigger onclick..
6262             return;
6263         }
6264         
6265         if(
6266                 this.preventDefault || 
6267                 this.href == '#' 
6268         ){
6269             Roo.log("NavItem - prevent Default?");
6270             e.preventDefault();
6271         }
6272         
6273         if (this.disabled) {
6274             return;
6275         }
6276         
6277         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6278         if (tg && tg.transition) {
6279             Roo.log("waiting for the transitionend");
6280             return;
6281         }
6282         
6283         
6284         
6285         //Roo.log("fire event clicked");
6286         if(this.fireEvent('click', this, e) === false){
6287             return;
6288         };
6289         
6290         if(this.tagtype == 'span'){
6291             return;
6292         }
6293         
6294         //Roo.log(this.href);
6295         var ael = this.el.select('a',true).first();
6296         //Roo.log(ael);
6297         
6298         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6299             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6300             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6301                 return; // ignore... - it's a 'hash' to another page.
6302             }
6303             Roo.log("NavItem - prevent Default?");
6304             e.preventDefault();
6305             this.scrollToElement(e);
6306         }
6307         
6308         
6309         var p =  this.parent();
6310    
6311         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6312             if (typeof(p.setActiveItem) !== 'undefined') {
6313                 p.setActiveItem(this);
6314             }
6315         }
6316         
6317         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6318         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6319             // remove the collapsed menu expand...
6320             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6321         }
6322     },
6323     
6324     isActive: function () {
6325         return this.active
6326     },
6327     setActive : function(state, fire, is_was_active)
6328     {
6329         if (this.active && !state && this.navId) {
6330             this.was_active = true;
6331             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6332             if (nv) {
6333                 nv.clearWasActive(this);
6334             }
6335             
6336         }
6337         this.active = state;
6338         
6339         if (!state ) {
6340             this.el.removeClass('active');
6341             this.navLink ? this.navLink.removeClass('active') : false;
6342         } else if (!this.el.hasClass('active')) {
6343             
6344             this.el.addClass('active');
6345             if (Roo.bootstrap.version == 4 && this.navLink ) {
6346                 this.navLink.addClass('active');
6347             }
6348             
6349         }
6350         if (fire) {
6351             this.fireEvent('changed', this, state);
6352         }
6353         
6354         // show a panel if it's registered and related..
6355         
6356         if (!this.navId || !this.tabId || !state || is_was_active) {
6357             return;
6358         }
6359         
6360         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6361         if (!tg) {
6362             return;
6363         }
6364         var pan = tg.getPanelByName(this.tabId);
6365         if (!pan) {
6366             return;
6367         }
6368         // if we can not flip to new panel - go back to old nav highlight..
6369         if (false == tg.showPanel(pan)) {
6370             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6371             if (nv) {
6372                 var onav = nv.getWasActive();
6373                 if (onav) {
6374                     onav.setActive(true, false, true);
6375                 }
6376             }
6377             
6378         }
6379         
6380         
6381         
6382     },
6383      // this should not be here...
6384     setDisabled : function(state)
6385     {
6386         this.disabled = state;
6387         if (!state ) {
6388             this.el.removeClass('disabled');
6389         } else if (!this.el.hasClass('disabled')) {
6390             this.el.addClass('disabled');
6391         }
6392         
6393     },
6394     
6395     /**
6396      * Fetch the element to display the tooltip on.
6397      * @return {Roo.Element} defaults to this.el
6398      */
6399     tooltipEl : function()
6400     {
6401         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6402     },
6403     
6404     scrollToElement : function(e)
6405     {
6406         var c = document.body;
6407         
6408         /*
6409          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6410          */
6411         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6412             c = document.documentElement;
6413         }
6414         
6415         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6416         
6417         if(!target){
6418             return;
6419         }
6420
6421         var o = target.calcOffsetsTo(c);
6422         
6423         var options = {
6424             target : target,
6425             value : o[1]
6426         };
6427         
6428         this.fireEvent('scrollto', this, options, e);
6429         
6430         Roo.get(c).scrollTo('top', options.value, true);
6431         
6432         return;
6433     }
6434 });
6435  
6436
6437  /*
6438  * - LGPL
6439  *
6440  * sidebar item
6441  *
6442  *  li
6443  *    <span> icon </span>
6444  *    <span> text </span>
6445  *    <span>badge </span>
6446  */
6447
6448 /**
6449  * @class Roo.bootstrap.NavSidebarItem
6450  * @extends Roo.bootstrap.NavItem
6451  * Bootstrap Navbar.NavSidebarItem class
6452  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6453  * {Boolean} open is the menu open
6454  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6455  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6456  * {String} buttonSize (sm|md|lg)the extra classes for the button
6457  * {Boolean} showArrow show arrow next to the text (default true)
6458  * @constructor
6459  * Create a new Navbar Button
6460  * @param {Object} config The config object
6461  */
6462 Roo.bootstrap.NavSidebarItem = function(config){
6463     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6464     this.addEvents({
6465         // raw events
6466         /**
6467          * @event click
6468          * The raw click event for the entire grid.
6469          * @param {Roo.EventObject} e
6470          */
6471         "click" : true,
6472          /**
6473             * @event changed
6474             * Fires when the active item active state changes
6475             * @param {Roo.bootstrap.NavSidebarItem} this
6476             * @param {boolean} state the new state
6477              
6478          */
6479         'changed': true
6480     });
6481    
6482 };
6483
6484 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6485     
6486     badgeWeight : 'default',
6487     
6488     open: false,
6489     
6490     buttonView : false,
6491     
6492     buttonWeight : 'default',
6493     
6494     buttonSize : 'md',
6495     
6496     showArrow : true,
6497     
6498     getAutoCreate : function(){
6499         
6500         
6501         var a = {
6502                 tag: 'a',
6503                 href : this.href || '#',
6504                 cls: '',
6505                 html : '',
6506                 cn : []
6507         };
6508         
6509         if(this.buttonView){
6510             a = {
6511                 tag: 'button',
6512                 href : this.href || '#',
6513                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6514                 html : this.html,
6515                 cn : []
6516             };
6517         }
6518         
6519         var cfg = {
6520             tag: 'li',
6521             cls: '',
6522             cn: [ a ]
6523         };
6524         
6525         if (this.active) {
6526             cfg.cls += ' active';
6527         }
6528         
6529         if (this.disabled) {
6530             cfg.cls += ' disabled';
6531         }
6532         if (this.open) {
6533             cfg.cls += ' open x-open';
6534         }
6535         // left icon..
6536         if (this.glyphicon || this.icon) {
6537             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6538             a.cn.push({ tag : 'i', cls : c }) ;
6539         }
6540         
6541         if(!this.buttonView){
6542             var span = {
6543                 tag: 'span',
6544                 html : this.html || ''
6545             };
6546
6547             a.cn.push(span);
6548             
6549         }
6550         
6551         if (this.badge !== '') {
6552             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6553         }
6554         
6555         if (this.menu) {
6556             
6557             if(this.showArrow){
6558                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6559             }
6560             
6561             a.cls += ' dropdown-toggle treeview' ;
6562         }
6563         
6564         return cfg;
6565     },
6566     
6567     initEvents : function()
6568     { 
6569         if (typeof (this.menu) != 'undefined') {
6570             this.menu.parentType = this.xtype;
6571             this.menu.triggerEl = this.el;
6572             this.menu = this.addxtype(Roo.apply({}, this.menu));
6573         }
6574         
6575         this.el.on('click', this.onClick, this);
6576         
6577         if(this.badge !== ''){
6578             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6579         }
6580         
6581     },
6582     
6583     onClick : function(e)
6584     {
6585         if(this.disabled){
6586             e.preventDefault();
6587             return;
6588         }
6589         
6590         if(this.preventDefault){
6591             e.preventDefault();
6592         }
6593         
6594         this.fireEvent('click', this, e);
6595     },
6596     
6597     disable : function()
6598     {
6599         this.setDisabled(true);
6600     },
6601     
6602     enable : function()
6603     {
6604         this.setDisabled(false);
6605     },
6606     
6607     setDisabled : function(state)
6608     {
6609         if(this.disabled == state){
6610             return;
6611         }
6612         
6613         this.disabled = state;
6614         
6615         if (state) {
6616             this.el.addClass('disabled');
6617             return;
6618         }
6619         
6620         this.el.removeClass('disabled');
6621         
6622         return;
6623     },
6624     
6625     setActive : function(state)
6626     {
6627         if(this.active == state){
6628             return;
6629         }
6630         
6631         this.active = state;
6632         
6633         if (state) {
6634             this.el.addClass('active');
6635             return;
6636         }
6637         
6638         this.el.removeClass('active');
6639         
6640         return;
6641     },
6642     
6643     isActive: function () 
6644     {
6645         return this.active;
6646     },
6647     
6648     setBadge : function(str)
6649     {
6650         if(!this.badgeEl){
6651             return;
6652         }
6653         
6654         this.badgeEl.dom.innerHTML = str;
6655     }
6656     
6657    
6658      
6659  
6660 });
6661  
6662
6663  /*
6664  * - LGPL
6665  *
6666  *  Breadcrumb Nav
6667  * 
6668  */
6669 Roo.namespace('Roo.bootstrap.breadcrumb');
6670
6671
6672 /**
6673  * @class Roo.bootstrap.breadcrumb.Nav
6674  * @extends Roo.bootstrap.Component
6675  * Bootstrap Breadcrumb Nav Class
6676  *  
6677  * @children Roo.bootstrap.breadcrumb.Item
6678  * 
6679  * @constructor
6680  * Create a new breadcrumb.Nav
6681  * @param {Object} config The config object
6682  */
6683
6684
6685 Roo.bootstrap.breadcrumb.Nav = function(config){
6686     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6687     
6688     
6689 };
6690
6691 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6692     
6693     getAutoCreate : function()
6694     {
6695
6696         var cfg = {
6697             tag: 'nav',
6698             cn : [
6699                 {
6700                     tag : 'ol',
6701                     cls : 'breadcrumb'
6702                 }
6703             ]
6704             
6705         };
6706           
6707         return cfg;
6708     },
6709     
6710     initEvents: function()
6711     {
6712         this.olEl = this.el.select('ol',true).first();    
6713     },
6714     getChildContainer : function()
6715     {
6716         return this.olEl;  
6717     }
6718     
6719 });
6720
6721  /*
6722  * - LGPL
6723  *
6724  *  Breadcrumb Item
6725  * 
6726  */
6727
6728
6729 /**
6730  * @class Roo.bootstrap.breadcrumb.Nav
6731  * @extends Roo.bootstrap.Component
6732  * Bootstrap Breadcrumb Nav Class
6733  *  
6734  * @children Roo.bootstrap.breadcrumb.Component
6735  * @cfg {String} html the content of the link.
6736  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6737  * @cfg {Boolean} active is it active
6738
6739  * 
6740  * @constructor
6741  * Create a new breadcrumb.Nav
6742  * @param {Object} config The config object
6743  */
6744
6745 Roo.bootstrap.breadcrumb.Item = function(config){
6746     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6747     this.addEvents({
6748         // img events
6749         /**
6750          * @event click
6751          * The img click event for the img.
6752          * @param {Roo.EventObject} e
6753          */
6754         "click" : true
6755     });
6756     
6757 };
6758
6759 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6760     
6761     href: false,
6762     html : '',
6763     
6764     getAutoCreate : function()
6765     {
6766
6767         var cfg = {
6768             tag: 'li',
6769             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6770         };
6771         if (this.href !== false) {
6772             cfg.cn = [{
6773                 tag : 'a',
6774                 href : this.href,
6775                 html : this.html
6776             }];
6777         } else {
6778             cfg.html = this.html;
6779         }
6780         
6781         return cfg;
6782     },
6783     
6784     initEvents: function()
6785     {
6786         if (this.href) {
6787             this.el.select('a', true).first().on('click',this.onClick, this)
6788         }
6789         
6790     },
6791     onClick : function(e)
6792     {
6793         e.preventDefault();
6794         this.fireEvent('click',this,  e);
6795     }
6796     
6797 });
6798
6799  /*
6800  * - LGPL
6801  *
6802  * row
6803  * 
6804  */
6805
6806 /**
6807  * @class Roo.bootstrap.Row
6808  * @extends Roo.bootstrap.Component
6809  * Bootstrap Row class (contains columns...)
6810  * 
6811  * @constructor
6812  * Create a new Row
6813  * @param {Object} config The config object
6814  */
6815
6816 Roo.bootstrap.Row = function(config){
6817     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6818 };
6819
6820 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6821     
6822     getAutoCreate : function(){
6823        return {
6824             cls: 'row clearfix'
6825        };
6826     }
6827     
6828     
6829 });
6830
6831  
6832
6833  /*
6834  * - LGPL
6835  *
6836  * pagination
6837  * 
6838  */
6839
6840 /**
6841  * @class Roo.bootstrap.Pagination
6842  * @extends Roo.bootstrap.Component
6843  * Bootstrap Pagination class
6844  * @cfg {String} size xs | sm | md | lg
6845  * @cfg {Boolean} inverse false | true
6846  * 
6847  * @constructor
6848  * Create a new Pagination
6849  * @param {Object} config The config object
6850  */
6851
6852 Roo.bootstrap.Pagination = function(config){
6853     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6854 };
6855
6856 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6857     
6858     cls: false,
6859     size: false,
6860     inverse: false,
6861     
6862     getAutoCreate : function(){
6863         var cfg = {
6864             tag: 'ul',
6865                 cls: 'pagination'
6866         };
6867         if (this.inverse) {
6868             cfg.cls += ' inverse';
6869         }
6870         if (this.html) {
6871             cfg.html=this.html;
6872         }
6873         if (this.cls) {
6874             cfg.cls += " " + this.cls;
6875         }
6876         return cfg;
6877     }
6878    
6879 });
6880
6881  
6882
6883  /*
6884  * - LGPL
6885  *
6886  * Pagination item
6887  * 
6888  */
6889
6890
6891 /**
6892  * @class Roo.bootstrap.PaginationItem
6893  * @extends Roo.bootstrap.Component
6894  * Bootstrap PaginationItem class
6895  * @cfg {String} html text
6896  * @cfg {String} href the link
6897  * @cfg {Boolean} preventDefault (true | false) default true
6898  * @cfg {Boolean} active (true | false) default false
6899  * @cfg {Boolean} disabled default false
6900  * 
6901  * 
6902  * @constructor
6903  * Create a new PaginationItem
6904  * @param {Object} config The config object
6905  */
6906
6907
6908 Roo.bootstrap.PaginationItem = function(config){
6909     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6910     this.addEvents({
6911         // raw events
6912         /**
6913          * @event click
6914          * The raw click event for the entire grid.
6915          * @param {Roo.EventObject} e
6916          */
6917         "click" : true
6918     });
6919 };
6920
6921 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6922     
6923     href : false,
6924     html : false,
6925     preventDefault: true,
6926     active : false,
6927     cls : false,
6928     disabled: false,
6929     
6930     getAutoCreate : function(){
6931         var cfg= {
6932             tag: 'li',
6933             cn: [
6934                 {
6935                     tag : 'a',
6936                     href : this.href ? this.href : '#',
6937                     html : this.html ? this.html : ''
6938                 }
6939             ]
6940         };
6941         
6942         if(this.cls){
6943             cfg.cls = this.cls;
6944         }
6945         
6946         if(this.disabled){
6947             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6948         }
6949         
6950         if(this.active){
6951             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6952         }
6953         
6954         return cfg;
6955     },
6956     
6957     initEvents: function() {
6958         
6959         this.el.on('click', this.onClick, this);
6960         
6961     },
6962     onClick : function(e)
6963     {
6964         Roo.log('PaginationItem on click ');
6965         if(this.preventDefault){
6966             e.preventDefault();
6967         }
6968         
6969         if(this.disabled){
6970             return;
6971         }
6972         
6973         this.fireEvent('click', this, e);
6974     }
6975    
6976 });
6977
6978  
6979
6980  /*
6981  * - LGPL
6982  *
6983  * slider
6984  * 
6985  */
6986
6987
6988 /**
6989  * @class Roo.bootstrap.Slider
6990  * @extends Roo.bootstrap.Component
6991  * Bootstrap Slider class
6992  *    
6993  * @constructor
6994  * Create a new Slider
6995  * @param {Object} config The config object
6996  */
6997
6998 Roo.bootstrap.Slider = function(config){
6999     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7000 };
7001
7002 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7003     
7004     getAutoCreate : function(){
7005         
7006         var cfg = {
7007             tag: 'div',
7008             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7009             cn: [
7010                 {
7011                     tag: 'a',
7012                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7013                 }
7014             ]
7015         };
7016         
7017         return cfg;
7018     }
7019    
7020 });
7021
7022  /*
7023  * Based on:
7024  * Ext JS Library 1.1.1
7025  * Copyright(c) 2006-2007, Ext JS, LLC.
7026  *
7027  * Originally Released Under LGPL - original licence link has changed is not relivant.
7028  *
7029  * Fork - LGPL
7030  * <script type="text/javascript">
7031  */
7032  
7033
7034 /**
7035  * @class Roo.grid.ColumnModel
7036  * @extends Roo.util.Observable
7037  * This is the default implementation of a ColumnModel used by the Grid. It defines
7038  * the columns in the grid.
7039  * <br>Usage:<br>
7040  <pre><code>
7041  var colModel = new Roo.grid.ColumnModel([
7042         {header: "Ticker", width: 60, sortable: true, locked: true},
7043         {header: "Company Name", width: 150, sortable: true},
7044         {header: "Market Cap.", width: 100, sortable: true},
7045         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7046         {header: "Employees", width: 100, sortable: true, resizable: false}
7047  ]);
7048  </code></pre>
7049  * <p>
7050  
7051  * The config options listed for this class are options which may appear in each
7052  * individual column definition.
7053  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7054  * @constructor
7055  * @param {Object} config An Array of column config objects. See this class's
7056  * config objects for details.
7057 */
7058 Roo.grid.ColumnModel = function(config){
7059         /**
7060      * The config passed into the constructor
7061      */
7062     this.config = config;
7063     this.lookup = {};
7064
7065     // if no id, create one
7066     // if the column does not have a dataIndex mapping,
7067     // map it to the order it is in the config
7068     for(var i = 0, len = config.length; i < len; i++){
7069         var c = config[i];
7070         if(typeof c.dataIndex == "undefined"){
7071             c.dataIndex = i;
7072         }
7073         if(typeof c.renderer == "string"){
7074             c.renderer = Roo.util.Format[c.renderer];
7075         }
7076         if(typeof c.id == "undefined"){
7077             c.id = Roo.id();
7078         }
7079         if(c.editor && c.editor.xtype){
7080             c.editor  = Roo.factory(c.editor, Roo.grid);
7081         }
7082         if(c.editor && c.editor.isFormField){
7083             c.editor = new Roo.grid.GridEditor(c.editor);
7084         }
7085         this.lookup[c.id] = c;
7086     }
7087
7088     /**
7089      * The width of columns which have no width specified (defaults to 100)
7090      * @type Number
7091      */
7092     this.defaultWidth = 100;
7093
7094     /**
7095      * Default sortable of columns which have no sortable specified (defaults to false)
7096      * @type Boolean
7097      */
7098     this.defaultSortable = false;
7099
7100     this.addEvents({
7101         /**
7102              * @event widthchange
7103              * Fires when the width of a column changes.
7104              * @param {ColumnModel} this
7105              * @param {Number} columnIndex The column index
7106              * @param {Number} newWidth The new width
7107              */
7108             "widthchange": true,
7109         /**
7110              * @event headerchange
7111              * Fires when the text of a header changes.
7112              * @param {ColumnModel} this
7113              * @param {Number} columnIndex The column index
7114              * @param {Number} newText The new header text
7115              */
7116             "headerchange": true,
7117         /**
7118              * @event hiddenchange
7119              * Fires when a column is hidden or "unhidden".
7120              * @param {ColumnModel} this
7121              * @param {Number} columnIndex The column index
7122              * @param {Boolean} hidden true if hidden, false otherwise
7123              */
7124             "hiddenchange": true,
7125             /**
7126          * @event columnmoved
7127          * Fires when a column is moved.
7128          * @param {ColumnModel} this
7129          * @param {Number} oldIndex
7130          * @param {Number} newIndex
7131          */
7132         "columnmoved" : true,
7133         /**
7134          * @event columlockchange
7135          * Fires when a column's locked state is changed
7136          * @param {ColumnModel} this
7137          * @param {Number} colIndex
7138          * @param {Boolean} locked true if locked
7139          */
7140         "columnlockchange" : true
7141     });
7142     Roo.grid.ColumnModel.superclass.constructor.call(this);
7143 };
7144 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7145     /**
7146      * @cfg {String} header The header text to display in the Grid view.
7147      */
7148     /**
7149      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7150      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7151      * specified, the column's index is used as an index into the Record's data Array.
7152      */
7153     /**
7154      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7155      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7156      */
7157     /**
7158      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7159      * Defaults to the value of the {@link #defaultSortable} property.
7160      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7161      */
7162     /**
7163      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7164      */
7165     /**
7166      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7167      */
7168     /**
7169      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7170      */
7171     /**
7172      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7173      */
7174     /**
7175      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7176      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7177      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7178      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7179      */
7180        /**
7181      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7182      */
7183     /**
7184      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7185      */
7186     /**
7187      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7188      */
7189     /**
7190      * @cfg {String} cursor (Optional)
7191      */
7192     /**
7193      * @cfg {String} tooltip (Optional)
7194      */
7195     /**
7196      * @cfg {Number} xs (Optional)
7197      */
7198     /**
7199      * @cfg {Number} sm (Optional)
7200      */
7201     /**
7202      * @cfg {Number} md (Optional)
7203      */
7204     /**
7205      * @cfg {Number} lg (Optional)
7206      */
7207     /**
7208      * Returns the id of the column at the specified index.
7209      * @param {Number} index The column index
7210      * @return {String} the id
7211      */
7212     getColumnId : function(index){
7213         return this.config[index].id;
7214     },
7215
7216     /**
7217      * Returns the column for a specified id.
7218      * @param {String} id The column id
7219      * @return {Object} the column
7220      */
7221     getColumnById : function(id){
7222         return this.lookup[id];
7223     },
7224
7225     
7226     /**
7227      * Returns the column for a specified dataIndex.
7228      * @param {String} dataIndex The column dataIndex
7229      * @return {Object|Boolean} the column or false if not found
7230      */
7231     getColumnByDataIndex: function(dataIndex){
7232         var index = this.findColumnIndex(dataIndex);
7233         return index > -1 ? this.config[index] : false;
7234     },
7235     
7236     /**
7237      * Returns the index for a specified column id.
7238      * @param {String} id The column id
7239      * @return {Number} the index, or -1 if not found
7240      */
7241     getIndexById : function(id){
7242         for(var i = 0, len = this.config.length; i < len; i++){
7243             if(this.config[i].id == id){
7244                 return i;
7245             }
7246         }
7247         return -1;
7248     },
7249     
7250     /**
7251      * Returns the index for a specified column dataIndex.
7252      * @param {String} dataIndex The column dataIndex
7253      * @return {Number} the index, or -1 if not found
7254      */
7255     
7256     findColumnIndex : function(dataIndex){
7257         for(var i = 0, len = this.config.length; i < len; i++){
7258             if(this.config[i].dataIndex == dataIndex){
7259                 return i;
7260             }
7261         }
7262         return -1;
7263     },
7264     
7265     
7266     moveColumn : function(oldIndex, newIndex){
7267         var c = this.config[oldIndex];
7268         this.config.splice(oldIndex, 1);
7269         this.config.splice(newIndex, 0, c);
7270         this.dataMap = null;
7271         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7272     },
7273
7274     isLocked : function(colIndex){
7275         return this.config[colIndex].locked === true;
7276     },
7277
7278     setLocked : function(colIndex, value, suppressEvent){
7279         if(this.isLocked(colIndex) == value){
7280             return;
7281         }
7282         this.config[colIndex].locked = value;
7283         if(!suppressEvent){
7284             this.fireEvent("columnlockchange", this, colIndex, value);
7285         }
7286     },
7287
7288     getTotalLockedWidth : function(){
7289         var totalWidth = 0;
7290         for(var i = 0; i < this.config.length; i++){
7291             if(this.isLocked(i) && !this.isHidden(i)){
7292                 this.totalWidth += this.getColumnWidth(i);
7293             }
7294         }
7295         return totalWidth;
7296     },
7297
7298     getLockedCount : function(){
7299         for(var i = 0, len = this.config.length; i < len; i++){
7300             if(!this.isLocked(i)){
7301                 return i;
7302             }
7303         }
7304         
7305         return this.config.length;
7306     },
7307
7308     /**
7309      * Returns the number of columns.
7310      * @return {Number}
7311      */
7312     getColumnCount : function(visibleOnly){
7313         if(visibleOnly === true){
7314             var c = 0;
7315             for(var i = 0, len = this.config.length; i < len; i++){
7316                 if(!this.isHidden(i)){
7317                     c++;
7318                 }
7319             }
7320             return c;
7321         }
7322         return this.config.length;
7323     },
7324
7325     /**
7326      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7327      * @param {Function} fn
7328      * @param {Object} scope (optional)
7329      * @return {Array} result
7330      */
7331     getColumnsBy : function(fn, scope){
7332         var r = [];
7333         for(var i = 0, len = this.config.length; i < len; i++){
7334             var c = this.config[i];
7335             if(fn.call(scope||this, c, i) === true){
7336                 r[r.length] = c;
7337             }
7338         }
7339         return r;
7340     },
7341
7342     /**
7343      * Returns true if the specified column is sortable.
7344      * @param {Number} col The column index
7345      * @return {Boolean}
7346      */
7347     isSortable : function(col){
7348         if(typeof this.config[col].sortable == "undefined"){
7349             return this.defaultSortable;
7350         }
7351         return this.config[col].sortable;
7352     },
7353
7354     /**
7355      * Returns the rendering (formatting) function defined for the column.
7356      * @param {Number} col The column index.
7357      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7358      */
7359     getRenderer : function(col){
7360         if(!this.config[col].renderer){
7361             return Roo.grid.ColumnModel.defaultRenderer;
7362         }
7363         return this.config[col].renderer;
7364     },
7365
7366     /**
7367      * Sets the rendering (formatting) function for a column.
7368      * @param {Number} col The column index
7369      * @param {Function} fn The function to use to process the cell's raw data
7370      * to return HTML markup for the grid view. The render function is called with
7371      * the following parameters:<ul>
7372      * <li>Data value.</li>
7373      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7374      * <li>css A CSS style string to apply to the table cell.</li>
7375      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7376      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7377      * <li>Row index</li>
7378      * <li>Column index</li>
7379      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7380      */
7381     setRenderer : function(col, fn){
7382         this.config[col].renderer = fn;
7383     },
7384
7385     /**
7386      * Returns the width for the specified column.
7387      * @param {Number} col The column index
7388      * @return {Number}
7389      */
7390     getColumnWidth : function(col){
7391         return this.config[col].width * 1 || this.defaultWidth;
7392     },
7393
7394     /**
7395      * Sets the width for a column.
7396      * @param {Number} col The column index
7397      * @param {Number} width The new width
7398      */
7399     setColumnWidth : function(col, width, suppressEvent){
7400         this.config[col].width = width;
7401         this.totalWidth = null;
7402         if(!suppressEvent){
7403              this.fireEvent("widthchange", this, col, width);
7404         }
7405     },
7406
7407     /**
7408      * Returns the total width of all columns.
7409      * @param {Boolean} includeHidden True to include hidden column widths
7410      * @return {Number}
7411      */
7412     getTotalWidth : function(includeHidden){
7413         if(!this.totalWidth){
7414             this.totalWidth = 0;
7415             for(var i = 0, len = this.config.length; i < len; i++){
7416                 if(includeHidden || !this.isHidden(i)){
7417                     this.totalWidth += this.getColumnWidth(i);
7418                 }
7419             }
7420         }
7421         return this.totalWidth;
7422     },
7423
7424     /**
7425      * Returns the header for the specified column.
7426      * @param {Number} col The column index
7427      * @return {String}
7428      */
7429     getColumnHeader : function(col){
7430         return this.config[col].header;
7431     },
7432
7433     /**
7434      * Sets the header for a column.
7435      * @param {Number} col The column index
7436      * @param {String} header The new header
7437      */
7438     setColumnHeader : function(col, header){
7439         this.config[col].header = header;
7440         this.fireEvent("headerchange", this, col, header);
7441     },
7442
7443     /**
7444      * Returns the tooltip for the specified column.
7445      * @param {Number} col The column index
7446      * @return {String}
7447      */
7448     getColumnTooltip : function(col){
7449             return this.config[col].tooltip;
7450     },
7451     /**
7452      * Sets the tooltip for a column.
7453      * @param {Number} col The column index
7454      * @param {String} tooltip The new tooltip
7455      */
7456     setColumnTooltip : function(col, tooltip){
7457             this.config[col].tooltip = tooltip;
7458     },
7459
7460     /**
7461      * Returns the dataIndex for the specified column.
7462      * @param {Number} col The column index
7463      * @return {Number}
7464      */
7465     getDataIndex : function(col){
7466         return this.config[col].dataIndex;
7467     },
7468
7469     /**
7470      * Sets the dataIndex for a column.
7471      * @param {Number} col The column index
7472      * @param {Number} dataIndex The new dataIndex
7473      */
7474     setDataIndex : function(col, dataIndex){
7475         this.config[col].dataIndex = dataIndex;
7476     },
7477
7478     
7479     
7480     /**
7481      * Returns true if the cell is editable.
7482      * @param {Number} colIndex The column index
7483      * @param {Number} rowIndex The row index - this is nto actually used..?
7484      * @return {Boolean}
7485      */
7486     isCellEditable : function(colIndex, rowIndex){
7487         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7488     },
7489
7490     /**
7491      * Returns the editor defined for the cell/column.
7492      * return false or null to disable editing.
7493      * @param {Number} colIndex The column index
7494      * @param {Number} rowIndex The row index
7495      * @return {Object}
7496      */
7497     getCellEditor : function(colIndex, rowIndex){
7498         return this.config[colIndex].editor;
7499     },
7500
7501     /**
7502      * Sets if a column is editable.
7503      * @param {Number} col The column index
7504      * @param {Boolean} editable True if the column is editable
7505      */
7506     setEditable : function(col, editable){
7507         this.config[col].editable = editable;
7508     },
7509
7510
7511     /**
7512      * Returns true if the column is hidden.
7513      * @param {Number} colIndex The column index
7514      * @return {Boolean}
7515      */
7516     isHidden : function(colIndex){
7517         return this.config[colIndex].hidden;
7518     },
7519
7520
7521     /**
7522      * Returns true if the column width cannot be changed
7523      */
7524     isFixed : function(colIndex){
7525         return this.config[colIndex].fixed;
7526     },
7527
7528     /**
7529      * Returns true if the column can be resized
7530      * @return {Boolean}
7531      */
7532     isResizable : function(colIndex){
7533         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7534     },
7535     /**
7536      * Sets if a column is hidden.
7537      * @param {Number} colIndex The column index
7538      * @param {Boolean} hidden True if the column is hidden
7539      */
7540     setHidden : function(colIndex, hidden){
7541         this.config[colIndex].hidden = hidden;
7542         this.totalWidth = null;
7543         this.fireEvent("hiddenchange", this, colIndex, hidden);
7544     },
7545
7546     /**
7547      * Sets the editor for a column.
7548      * @param {Number} col The column index
7549      * @param {Object} editor The editor object
7550      */
7551     setEditor : function(col, editor){
7552         this.config[col].editor = editor;
7553     }
7554 });
7555
7556 Roo.grid.ColumnModel.defaultRenderer = function(value)
7557 {
7558     if(typeof value == "object") {
7559         return value;
7560     }
7561         if(typeof value == "string" && value.length < 1){
7562             return "&#160;";
7563         }
7564     
7565         return String.format("{0}", value);
7566 };
7567
7568 // Alias for backwards compatibility
7569 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7570 /*
7571  * Based on:
7572  * Ext JS Library 1.1.1
7573  * Copyright(c) 2006-2007, Ext JS, LLC.
7574  *
7575  * Originally Released Under LGPL - original licence link has changed is not relivant.
7576  *
7577  * Fork - LGPL
7578  * <script type="text/javascript">
7579  */
7580  
7581 /**
7582  * @class Roo.LoadMask
7583  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7584  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7585  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7586  * element's UpdateManager load indicator and will be destroyed after the initial load.
7587  * @constructor
7588  * Create a new LoadMask
7589  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7590  * @param {Object} config The config object
7591  */
7592 Roo.LoadMask = function(el, config){
7593     this.el = Roo.get(el);
7594     Roo.apply(this, config);
7595     if(this.store){
7596         this.store.on('beforeload', this.onBeforeLoad, this);
7597         this.store.on('load', this.onLoad, this);
7598         this.store.on('loadexception', this.onLoadException, this);
7599         this.removeMask = false;
7600     }else{
7601         var um = this.el.getUpdateManager();
7602         um.showLoadIndicator = false; // disable the default indicator
7603         um.on('beforeupdate', this.onBeforeLoad, this);
7604         um.on('update', this.onLoad, this);
7605         um.on('failure', this.onLoad, this);
7606         this.removeMask = true;
7607     }
7608 };
7609
7610 Roo.LoadMask.prototype = {
7611     /**
7612      * @cfg {Boolean} removeMask
7613      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7614      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7615      */
7616     /**
7617      * @cfg {String} msg
7618      * The text to display in a centered loading message box (defaults to 'Loading...')
7619      */
7620     msg : 'Loading...',
7621     /**
7622      * @cfg {String} msgCls
7623      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7624      */
7625     msgCls : 'x-mask-loading',
7626
7627     /**
7628      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7629      * @type Boolean
7630      */
7631     disabled: false,
7632
7633     /**
7634      * Disables the mask to prevent it from being displayed
7635      */
7636     disable : function(){
7637        this.disabled = true;
7638     },
7639
7640     /**
7641      * Enables the mask so that it can be displayed
7642      */
7643     enable : function(){
7644         this.disabled = false;
7645     },
7646     
7647     onLoadException : function()
7648     {
7649         Roo.log(arguments);
7650         
7651         if (typeof(arguments[3]) != 'undefined') {
7652             Roo.MessageBox.alert("Error loading",arguments[3]);
7653         } 
7654         /*
7655         try {
7656             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7657                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7658             }   
7659         } catch(e) {
7660             
7661         }
7662         */
7663     
7664         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7665     },
7666     // private
7667     onLoad : function()
7668     {
7669         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7670     },
7671
7672     // private
7673     onBeforeLoad : function(){
7674         if(!this.disabled){
7675             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7676         }
7677     },
7678
7679     // private
7680     destroy : function(){
7681         if(this.store){
7682             this.store.un('beforeload', this.onBeforeLoad, this);
7683             this.store.un('load', this.onLoad, this);
7684             this.store.un('loadexception', this.onLoadException, this);
7685         }else{
7686             var um = this.el.getUpdateManager();
7687             um.un('beforeupdate', this.onBeforeLoad, this);
7688             um.un('update', this.onLoad, this);
7689             um.un('failure', this.onLoad, this);
7690         }
7691     }
7692 };/*
7693  * - LGPL
7694  *
7695  * table
7696  * 
7697  */
7698
7699 /**
7700  * @class Roo.bootstrap.Table
7701  * @extends Roo.bootstrap.Component
7702  * Bootstrap Table class
7703  * @cfg {String} cls table class
7704  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7705  * @cfg {String} bgcolor Specifies the background color for a table
7706  * @cfg {Number} border Specifies whether the table cells should have borders or not
7707  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7708  * @cfg {Number} cellspacing Specifies the space between cells
7709  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7710  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7711  * @cfg {String} sortable Specifies that the table should be sortable
7712  * @cfg {String} summary Specifies a summary of the content of a table
7713  * @cfg {Number} width Specifies the width of a table
7714  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7715  * 
7716  * @cfg {boolean} striped Should the rows be alternative striped
7717  * @cfg {boolean} bordered Add borders to the table
7718  * @cfg {boolean} hover Add hover highlighting
7719  * @cfg {boolean} condensed Format condensed
7720  * @cfg {boolean} responsive Format condensed
7721  * @cfg {Boolean} loadMask (true|false) default false
7722  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7723  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7724  * @cfg {Boolean} rowSelection (true|false) default false
7725  * @cfg {Boolean} cellSelection (true|false) default false
7726  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7727  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7728  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7729  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7730  
7731  * 
7732  * @constructor
7733  * Create a new Table
7734  * @param {Object} config The config object
7735  */
7736
7737 Roo.bootstrap.Table = function(config){
7738     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7739     
7740   
7741     
7742     // BC...
7743     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7744     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7745     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7746     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7747     
7748     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7749     if (this.sm) {
7750         this.sm.grid = this;
7751         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7752         this.sm = this.selModel;
7753         this.sm.xmodule = this.xmodule || false;
7754     }
7755     
7756     if (this.cm && typeof(this.cm.config) == 'undefined') {
7757         this.colModel = new Roo.grid.ColumnModel(this.cm);
7758         this.cm = this.colModel;
7759         this.cm.xmodule = this.xmodule || false;
7760     }
7761     if (this.store) {
7762         this.store= Roo.factory(this.store, Roo.data);
7763         this.ds = this.store;
7764         this.ds.xmodule = this.xmodule || false;
7765          
7766     }
7767     if (this.footer && this.store) {
7768         this.footer.dataSource = this.ds;
7769         this.footer = Roo.factory(this.footer);
7770     }
7771     
7772     /** @private */
7773     this.addEvents({
7774         /**
7775          * @event cellclick
7776          * Fires when a cell is clicked
7777          * @param {Roo.bootstrap.Table} this
7778          * @param {Roo.Element} el
7779          * @param {Number} rowIndex
7780          * @param {Number} columnIndex
7781          * @param {Roo.EventObject} e
7782          */
7783         "cellclick" : true,
7784         /**
7785          * @event celldblclick
7786          * Fires when a cell is double clicked
7787          * @param {Roo.bootstrap.Table} this
7788          * @param {Roo.Element} el
7789          * @param {Number} rowIndex
7790          * @param {Number} columnIndex
7791          * @param {Roo.EventObject} e
7792          */
7793         "celldblclick" : true,
7794         /**
7795          * @event rowclick
7796          * Fires when a row is clicked
7797          * @param {Roo.bootstrap.Table} this
7798          * @param {Roo.Element} el
7799          * @param {Number} rowIndex
7800          * @param {Roo.EventObject} e
7801          */
7802         "rowclick" : true,
7803         /**
7804          * @event rowdblclick
7805          * Fires when a row is double clicked
7806          * @param {Roo.bootstrap.Table} this
7807          * @param {Roo.Element} el
7808          * @param {Number} rowIndex
7809          * @param {Roo.EventObject} e
7810          */
7811         "rowdblclick" : true,
7812         /**
7813          * @event mouseover
7814          * Fires when a mouseover occur
7815          * @param {Roo.bootstrap.Table} this
7816          * @param {Roo.Element} el
7817          * @param {Number} rowIndex
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "mouseover" : true,
7822         /**
7823          * @event mouseout
7824          * Fires when a mouseout occur
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Roo.Element} el
7827          * @param {Number} rowIndex
7828          * @param {Number} columnIndex
7829          * @param {Roo.EventObject} e
7830          */
7831         "mouseout" : true,
7832         /**
7833          * @event rowclass
7834          * Fires when a row is rendered, so you can change add a style to it.
7835          * @param {Roo.bootstrap.Table} this
7836          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7837          */
7838         'rowclass' : true,
7839           /**
7840          * @event rowsrendered
7841          * Fires when all the  rows have been rendered
7842          * @param {Roo.bootstrap.Table} this
7843          */
7844         'rowsrendered' : true,
7845         /**
7846          * @event contextmenu
7847          * The raw contextmenu event for the entire grid.
7848          * @param {Roo.EventObject} e
7849          */
7850         "contextmenu" : true,
7851         /**
7852          * @event rowcontextmenu
7853          * Fires when a row is right clicked
7854          * @param {Roo.bootstrap.Table} this
7855          * @param {Number} rowIndex
7856          * @param {Roo.EventObject} e
7857          */
7858         "rowcontextmenu" : true,
7859         /**
7860          * @event cellcontextmenu
7861          * Fires when a cell is right clicked
7862          * @param {Roo.bootstrap.Table} this
7863          * @param {Number} rowIndex
7864          * @param {Number} cellIndex
7865          * @param {Roo.EventObject} e
7866          */
7867          "cellcontextmenu" : true,
7868          /**
7869          * @event headercontextmenu
7870          * Fires when a header is right clicked
7871          * @param {Roo.bootstrap.Table} this
7872          * @param {Number} columnIndex
7873          * @param {Roo.EventObject} e
7874          */
7875         "headercontextmenu" : true
7876     });
7877 };
7878
7879 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7880     
7881     cls: false,
7882     align: false,
7883     bgcolor: false,
7884     border: false,
7885     cellpadding: false,
7886     cellspacing: false,
7887     frame: false,
7888     rules: false,
7889     sortable: false,
7890     summary: false,
7891     width: false,
7892     striped : false,
7893     scrollBody : false,
7894     bordered: false,
7895     hover:  false,
7896     condensed : false,
7897     responsive : false,
7898     sm : false,
7899     cm : false,
7900     store : false,
7901     loadMask : false,
7902     footerShow : true,
7903     headerShow : true,
7904   
7905     rowSelection : false,
7906     cellSelection : false,
7907     layout : false,
7908     
7909     // Roo.Element - the tbody
7910     mainBody: false,
7911     // Roo.Element - thead element
7912     mainHead: false,
7913     
7914     container: false, // used by gridpanel...
7915     
7916     lazyLoad : false,
7917     
7918     CSS : Roo.util.CSS,
7919     
7920     auto_hide_footer : false,
7921     
7922     getAutoCreate : function()
7923     {
7924         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7925         
7926         cfg = {
7927             tag: 'table',
7928             cls : 'table',
7929             cn : []
7930         };
7931         if (this.scrollBody) {
7932             cfg.cls += ' table-body-fixed';
7933         }    
7934         if (this.striped) {
7935             cfg.cls += ' table-striped';
7936         }
7937         
7938         if (this.hover) {
7939             cfg.cls += ' table-hover';
7940         }
7941         if (this.bordered) {
7942             cfg.cls += ' table-bordered';
7943         }
7944         if (this.condensed) {
7945             cfg.cls += ' table-condensed';
7946         }
7947         if (this.responsive) {
7948             cfg.cls += ' table-responsive';
7949         }
7950         
7951         if (this.cls) {
7952             cfg.cls+=  ' ' +this.cls;
7953         }
7954         
7955         // this lot should be simplifed...
7956         var _t = this;
7957         var cp = [
7958             'align',
7959             'bgcolor',
7960             'border',
7961             'cellpadding',
7962             'cellspacing',
7963             'frame',
7964             'rules',
7965             'sortable',
7966             'summary',
7967             'width'
7968         ].forEach(function(k) {
7969             if (_t[k]) {
7970                 cfg[k] = _t[k];
7971             }
7972         });
7973         
7974         
7975         if (this.layout) {
7976             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7977         }
7978         
7979         if(this.store || this.cm){
7980             if(this.headerShow){
7981                 cfg.cn.push(this.renderHeader());
7982             }
7983             
7984             cfg.cn.push(this.renderBody());
7985             
7986             if(this.footerShow){
7987                 cfg.cn.push(this.renderFooter());
7988             }
7989             // where does this come from?
7990             //cfg.cls+=  ' TableGrid';
7991         }
7992         
7993         return { cn : [ cfg ] };
7994     },
7995     
7996     initEvents : function()
7997     {   
7998         if(!this.store || !this.cm){
7999             return;
8000         }
8001         if (this.selModel) {
8002             this.selModel.initEvents();
8003         }
8004         
8005         
8006         //Roo.log('initEvents with ds!!!!');
8007         
8008         this.mainBody = this.el.select('tbody', true).first();
8009         this.mainHead = this.el.select('thead', true).first();
8010         this.mainFoot = this.el.select('tfoot', true).first();
8011         
8012         
8013         
8014         var _this = this;
8015         
8016         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8017             e.on('click', _this.sort, _this);
8018         });
8019         
8020         this.mainBody.on("click", this.onClick, this);
8021         this.mainBody.on("dblclick", this.onDblClick, this);
8022         
8023         // why is this done????? = it breaks dialogs??
8024         //this.parent().el.setStyle('position', 'relative');
8025         
8026         
8027         if (this.footer) {
8028             this.footer.parentId = this.id;
8029             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8030             
8031             if(this.lazyLoad){
8032                 this.el.select('tfoot tr td').first().addClass('hide');
8033             }
8034         } 
8035         
8036         if(this.loadMask) {
8037             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8038         }
8039         
8040         this.store.on('load', this.onLoad, this);
8041         this.store.on('beforeload', this.onBeforeLoad, this);
8042         this.store.on('update', this.onUpdate, this);
8043         this.store.on('add', this.onAdd, this);
8044         this.store.on("clear", this.clear, this);
8045         
8046         this.el.on("contextmenu", this.onContextMenu, this);
8047         
8048         this.mainBody.on('scroll', this.onBodyScroll, this);
8049         
8050         this.cm.on("headerchange", this.onHeaderChange, this);
8051         
8052         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8053         
8054     },
8055     
8056     onContextMenu : function(e, t)
8057     {
8058         this.processEvent("contextmenu", e);
8059     },
8060     
8061     processEvent : function(name, e)
8062     {
8063         if (name != 'touchstart' ) {
8064             this.fireEvent(name, e);    
8065         }
8066         
8067         var t = e.getTarget();
8068         
8069         var cell = Roo.get(t);
8070         
8071         if(!cell){
8072             return;
8073         }
8074         
8075         if(cell.findParent('tfoot', false, true)){
8076             return;
8077         }
8078         
8079         if(cell.findParent('thead', false, true)){
8080             
8081             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8082                 cell = Roo.get(t).findParent('th', false, true);
8083                 if (!cell) {
8084                     Roo.log("failed to find th in thead?");
8085                     Roo.log(e.getTarget());
8086                     return;
8087                 }
8088             }
8089             
8090             var cellIndex = cell.dom.cellIndex;
8091             
8092             var ename = name == 'touchstart' ? 'click' : name;
8093             this.fireEvent("header" + ename, this, cellIndex, e);
8094             
8095             return;
8096         }
8097         
8098         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8099             cell = Roo.get(t).findParent('td', false, true);
8100             if (!cell) {
8101                 Roo.log("failed to find th in tbody?");
8102                 Roo.log(e.getTarget());
8103                 return;
8104             }
8105         }
8106         
8107         var row = cell.findParent('tr', false, true);
8108         var cellIndex = cell.dom.cellIndex;
8109         var rowIndex = row.dom.rowIndex - 1;
8110         
8111         if(row !== false){
8112             
8113             this.fireEvent("row" + name, this, rowIndex, e);
8114             
8115             if(cell !== false){
8116             
8117                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8118             }
8119         }
8120         
8121     },
8122     
8123     onMouseover : function(e, el)
8124     {
8125         var cell = Roo.get(el);
8126         
8127         if(!cell){
8128             return;
8129         }
8130         
8131         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8132             cell = cell.findParent('td', false, true);
8133         }
8134         
8135         var row = cell.findParent('tr', false, true);
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = row.dom.rowIndex - 1; // start from 0
8138         
8139         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8140         
8141     },
8142     
8143     onMouseout : function(e, el)
8144     {
8145         var cell = Roo.get(el);
8146         
8147         if(!cell){
8148             return;
8149         }
8150         
8151         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8152             cell = cell.findParent('td', false, true);
8153         }
8154         
8155         var row = cell.findParent('tr', false, true);
8156         var cellIndex = cell.dom.cellIndex;
8157         var rowIndex = row.dom.rowIndex - 1; // start from 0
8158         
8159         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8160         
8161     },
8162     
8163     onClick : function(e, el)
8164     {
8165         var cell = Roo.get(el);
8166         
8167         if(!cell || (!this.cellSelection && !this.rowSelection)){
8168             return;
8169         }
8170         
8171         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8172             cell = cell.findParent('td', false, true);
8173         }
8174         
8175         if(!cell || typeof(cell) == 'undefined'){
8176             return;
8177         }
8178         
8179         var row = cell.findParent('tr', false, true);
8180         
8181         if(!row || typeof(row) == 'undefined'){
8182             return;
8183         }
8184         
8185         var cellIndex = cell.dom.cellIndex;
8186         var rowIndex = this.getRowIndex(row);
8187         
8188         // why??? - should these not be based on SelectionModel?
8189         if(this.cellSelection){
8190             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8191         }
8192         
8193         if(this.rowSelection){
8194             this.fireEvent('rowclick', this, row, rowIndex, e);
8195         }
8196         
8197         
8198     },
8199         
8200     onDblClick : function(e,el)
8201     {
8202         var cell = Roo.get(el);
8203         
8204         if(!cell || (!this.cellSelection && !this.rowSelection)){
8205             return;
8206         }
8207         
8208         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8209             cell = cell.findParent('td', false, true);
8210         }
8211         
8212         if(!cell || typeof(cell) == 'undefined'){
8213             return;
8214         }
8215         
8216         var row = cell.findParent('tr', false, true);
8217         
8218         if(!row || typeof(row) == 'undefined'){
8219             return;
8220         }
8221         
8222         var cellIndex = cell.dom.cellIndex;
8223         var rowIndex = this.getRowIndex(row);
8224         
8225         if(this.cellSelection){
8226             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8227         }
8228         
8229         if(this.rowSelection){
8230             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8231         }
8232     },
8233     
8234     sort : function(e,el)
8235     {
8236         var col = Roo.get(el);
8237         
8238         if(!col.hasClass('sortable')){
8239             return;
8240         }
8241         
8242         var sort = col.attr('sort');
8243         var dir = 'ASC';
8244         
8245         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8246             dir = 'DESC';
8247         }
8248         
8249         this.store.sortInfo = {field : sort, direction : dir};
8250         
8251         if (this.footer) {
8252             Roo.log("calling footer first");
8253             this.footer.onClick('first');
8254         } else {
8255         
8256             this.store.load({ params : { start : 0 } });
8257         }
8258     },
8259     
8260     renderHeader : function()
8261     {
8262         var header = {
8263             tag: 'thead',
8264             cn : []
8265         };
8266         
8267         var cm = this.cm;
8268         this.totalWidth = 0;
8269         
8270         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8271             
8272             var config = cm.config[i];
8273             
8274             var c = {
8275                 tag: 'th',
8276                 cls : 'x-hcol-' + i,
8277                 style : '',
8278                 html: cm.getColumnHeader(i)
8279             };
8280             
8281             var hh = '';
8282             
8283             if(typeof(config.sortable) != 'undefined' && config.sortable){
8284                 c.cls = 'sortable';
8285                 c.html = '<i class="glyphicon"></i>' + c.html;
8286             }
8287             
8288             // could use BS4 hidden-..-down 
8289             
8290             if(typeof(config.lgHeader) != 'undefined'){
8291                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8292             }
8293             
8294             if(typeof(config.mdHeader) != 'undefined'){
8295                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8296             }
8297             
8298             if(typeof(config.smHeader) != 'undefined'){
8299                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8300             }
8301             
8302             if(typeof(config.xsHeader) != 'undefined'){
8303                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8304             }
8305             
8306             if(hh.length){
8307                 c.html = hh;
8308             }
8309             
8310             if(typeof(config.tooltip) != 'undefined'){
8311                 c.tooltip = config.tooltip;
8312             }
8313             
8314             if(typeof(config.colspan) != 'undefined'){
8315                 c.colspan = config.colspan;
8316             }
8317             
8318             if(typeof(config.hidden) != 'undefined' && config.hidden){
8319                 c.style += ' display:none;';
8320             }
8321             
8322             if(typeof(config.dataIndex) != 'undefined'){
8323                 c.sort = config.dataIndex;
8324             }
8325             
8326            
8327             
8328             if(typeof(config.align) != 'undefined' && config.align.length){
8329                 c.style += ' text-align:' + config.align + ';';
8330             }
8331             
8332             if(typeof(config.width) != 'undefined'){
8333                 c.style += ' width:' + config.width + 'px;';
8334                 this.totalWidth += config.width;
8335             } else {
8336                 this.totalWidth += 100; // assume minimum of 100 per column?
8337             }
8338             
8339             if(typeof(config.cls) != 'undefined'){
8340                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8341             }
8342             
8343             ['xs','sm','md','lg'].map(function(size){
8344                 
8345                 if(typeof(config[size]) == 'undefined'){
8346                     return;
8347                 }
8348                  
8349                 if (!config[size]) { // 0 = hidden
8350                     // BS 4 '0' is treated as hide that column and below.
8351                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8352                     return;
8353                 }
8354                 
8355                 c.cls += ' col-' + size + '-' + config[size] + (
8356                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8357                 );
8358                 
8359                 
8360             });
8361             
8362             header.cn.push(c)
8363         }
8364         
8365         return header;
8366     },
8367     
8368     renderBody : function()
8369     {
8370         var body = {
8371             tag: 'tbody',
8372             cn : [
8373                 {
8374                     tag: 'tr',
8375                     cn : [
8376                         {
8377                             tag : 'td',
8378                             colspan :  this.cm.getColumnCount()
8379                         }
8380                     ]
8381                 }
8382             ]
8383         };
8384         
8385         return body;
8386     },
8387     
8388     renderFooter : function()
8389     {
8390         var footer = {
8391             tag: 'tfoot',
8392             cn : [
8393                 {
8394                     tag: 'tr',
8395                     cn : [
8396                         {
8397                             tag : 'td',
8398                             colspan :  this.cm.getColumnCount()
8399                         }
8400                     ]
8401                 }
8402             ]
8403         };
8404         
8405         return footer;
8406     },
8407     
8408     
8409     
8410     onLoad : function()
8411     {
8412 //        Roo.log('ds onload');
8413         this.clear();
8414         
8415         var _this = this;
8416         var cm = this.cm;
8417         var ds = this.store;
8418         
8419         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8420             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8421             if (_this.store.sortInfo) {
8422                     
8423                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8424                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8425                 }
8426                 
8427                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8428                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8429                 }
8430             }
8431         });
8432         
8433         var tbody =  this.mainBody;
8434               
8435         if(ds.getCount() > 0){
8436             ds.data.each(function(d,rowIndex){
8437                 var row =  this.renderRow(cm, ds, rowIndex);
8438                 
8439                 tbody.createChild(row);
8440                 
8441                 var _this = this;
8442                 
8443                 if(row.cellObjects.length){
8444                     Roo.each(row.cellObjects, function(r){
8445                         _this.renderCellObject(r);
8446                     })
8447                 }
8448                 
8449             }, this);
8450         }
8451         
8452         var tfoot = this.el.select('tfoot', true).first();
8453         
8454         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8455             
8456             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8457             
8458             var total = this.ds.getTotalCount();
8459             
8460             if(this.footer.pageSize < total){
8461                 this.mainFoot.show();
8462             }
8463         }
8464         
8465         Roo.each(this.el.select('tbody td', true).elements, function(e){
8466             e.on('mouseover', _this.onMouseover, _this);
8467         });
8468         
8469         Roo.each(this.el.select('tbody td', true).elements, function(e){
8470             e.on('mouseout', _this.onMouseout, _this);
8471         });
8472         this.fireEvent('rowsrendered', this);
8473         
8474         this.autoSize();
8475     },
8476     
8477     
8478     onUpdate : function(ds,record)
8479     {
8480         this.refreshRow(record);
8481         this.autoSize();
8482     },
8483     
8484     onRemove : function(ds, record, index, isUpdate){
8485         if(isUpdate !== true){
8486             this.fireEvent("beforerowremoved", this, index, record);
8487         }
8488         var bt = this.mainBody.dom;
8489         
8490         var rows = this.el.select('tbody > tr', true).elements;
8491         
8492         if(typeof(rows[index]) != 'undefined'){
8493             bt.removeChild(rows[index].dom);
8494         }
8495         
8496 //        if(bt.rows[index]){
8497 //            bt.removeChild(bt.rows[index]);
8498 //        }
8499         
8500         if(isUpdate !== true){
8501             //this.stripeRows(index);
8502             //this.syncRowHeights(index, index);
8503             //this.layout();
8504             this.fireEvent("rowremoved", this, index, record);
8505         }
8506     },
8507     
8508     onAdd : function(ds, records, rowIndex)
8509     {
8510         //Roo.log('on Add called');
8511         // - note this does not handle multiple adding very well..
8512         var bt = this.mainBody.dom;
8513         for (var i =0 ; i < records.length;i++) {
8514             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8515             //Roo.log(records[i]);
8516             //Roo.log(this.store.getAt(rowIndex+i));
8517             this.insertRow(this.store, rowIndex + i, false);
8518             return;
8519         }
8520         
8521     },
8522     
8523     
8524     refreshRow : function(record){
8525         var ds = this.store, index;
8526         if(typeof record == 'number'){
8527             index = record;
8528             record = ds.getAt(index);
8529         }else{
8530             index = ds.indexOf(record);
8531             if (index < 0) {
8532                 return; // should not happen - but seems to 
8533             }
8534         }
8535         this.insertRow(ds, index, true);
8536         this.autoSize();
8537         this.onRemove(ds, record, index+1, true);
8538         this.autoSize();
8539         //this.syncRowHeights(index, index);
8540         //this.layout();
8541         this.fireEvent("rowupdated", this, index, record);
8542     },
8543     
8544     insertRow : function(dm, rowIndex, isUpdate){
8545         
8546         if(!isUpdate){
8547             this.fireEvent("beforerowsinserted", this, rowIndex);
8548         }
8549             //var s = this.getScrollState();
8550         var row = this.renderRow(this.cm, this.store, rowIndex);
8551         // insert before rowIndex..
8552         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8553         
8554         var _this = this;
8555                 
8556         if(row.cellObjects.length){
8557             Roo.each(row.cellObjects, function(r){
8558                 _this.renderCellObject(r);
8559             })
8560         }
8561             
8562         if(!isUpdate){
8563             this.fireEvent("rowsinserted", this, rowIndex);
8564             //this.syncRowHeights(firstRow, lastRow);
8565             //this.stripeRows(firstRow);
8566             //this.layout();
8567         }
8568         
8569     },
8570     
8571     
8572     getRowDom : function(rowIndex)
8573     {
8574         var rows = this.el.select('tbody > tr', true).elements;
8575         
8576         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8577         
8578     },
8579     // returns the object tree for a tr..
8580   
8581     
8582     renderRow : function(cm, ds, rowIndex) 
8583     {
8584         var d = ds.getAt(rowIndex);
8585         
8586         var row = {
8587             tag : 'tr',
8588             cls : 'x-row-' + rowIndex,
8589             cn : []
8590         };
8591             
8592         var cellObjects = [];
8593         
8594         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8595             var config = cm.config[i];
8596             
8597             var renderer = cm.getRenderer(i);
8598             var value = '';
8599             var id = false;
8600             
8601             if(typeof(renderer) !== 'undefined'){
8602                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8603             }
8604             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8605             // and are rendered into the cells after the row is rendered - using the id for the element.
8606             
8607             if(typeof(value) === 'object'){
8608                 id = Roo.id();
8609                 cellObjects.push({
8610                     container : id,
8611                     cfg : value 
8612                 })
8613             }
8614             
8615             var rowcfg = {
8616                 record: d,
8617                 rowIndex : rowIndex,
8618                 colIndex : i,
8619                 rowClass : ''
8620             };
8621
8622             this.fireEvent('rowclass', this, rowcfg);
8623             
8624             var td = {
8625                 tag: 'td',
8626                 cls : rowcfg.rowClass + ' x-col-' + i,
8627                 style: '',
8628                 html: (typeof(value) === 'object') ? '' : value
8629             };
8630             
8631             if (id) {
8632                 td.id = id;
8633             }
8634             
8635             if(typeof(config.colspan) != 'undefined'){
8636                 td.colspan = config.colspan;
8637             }
8638             
8639             if(typeof(config.hidden) != 'undefined' && config.hidden){
8640                 td.style += ' display:none;';
8641             }
8642             
8643             if(typeof(config.align) != 'undefined' && config.align.length){
8644                 td.style += ' text-align:' + config.align + ';';
8645             }
8646             if(typeof(config.valign) != 'undefined' && config.valign.length){
8647                 td.style += ' vertical-align:' + config.valign + ';';
8648             }
8649             
8650             if(typeof(config.width) != 'undefined'){
8651                 td.style += ' width:' +  config.width + 'px;';
8652             }
8653             
8654             if(typeof(config.cursor) != 'undefined'){
8655                 td.style += ' cursor:' +  config.cursor + ';';
8656             }
8657             
8658             if(typeof(config.cls) != 'undefined'){
8659                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8660             }
8661             
8662             ['xs','sm','md','lg'].map(function(size){
8663                 
8664                 if(typeof(config[size]) == 'undefined'){
8665                     return;
8666                 }
8667                 
8668                 
8669                   
8670                 if (!config[size]) { // 0 = hidden
8671                     // BS 4 '0' is treated as hide that column and below.
8672                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8673                     return;
8674                 }
8675                 
8676                 td.cls += ' col-' + size + '-' + config[size] + (
8677                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8678                 );
8679                  
8680
8681             });
8682             
8683             row.cn.push(td);
8684            
8685         }
8686         
8687         row.cellObjects = cellObjects;
8688         
8689         return row;
8690           
8691     },
8692     
8693     
8694     
8695     onBeforeLoad : function()
8696     {
8697         
8698     },
8699      /**
8700      * Remove all rows
8701      */
8702     clear : function()
8703     {
8704         this.el.select('tbody', true).first().dom.innerHTML = '';
8705     },
8706     /**
8707      * Show or hide a row.
8708      * @param {Number} rowIndex to show or hide
8709      * @param {Boolean} state hide
8710      */
8711     setRowVisibility : function(rowIndex, state)
8712     {
8713         var bt = this.mainBody.dom;
8714         
8715         var rows = this.el.select('tbody > tr', true).elements;
8716         
8717         if(typeof(rows[rowIndex]) == 'undefined'){
8718             return;
8719         }
8720         rows[rowIndex].dom.style.display = state ? '' : 'none';
8721     },
8722     
8723     
8724     getSelectionModel : function(){
8725         if(!this.selModel){
8726             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8727         }
8728         return this.selModel;
8729     },
8730     /*
8731      * Render the Roo.bootstrap object from renderder
8732      */
8733     renderCellObject : function(r)
8734     {
8735         var _this = this;
8736         
8737         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8738         
8739         var t = r.cfg.render(r.container);
8740         
8741         if(r.cfg.cn){
8742             Roo.each(r.cfg.cn, function(c){
8743                 var child = {
8744                     container: t.getChildContainer(),
8745                     cfg: c
8746                 };
8747                 _this.renderCellObject(child);
8748             })
8749         }
8750     },
8751     
8752     getRowIndex : function(row)
8753     {
8754         var rowIndex = -1;
8755         
8756         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8757             if(el != row){
8758                 return;
8759             }
8760             
8761             rowIndex = index;
8762         });
8763         
8764         return rowIndex;
8765     },
8766      /**
8767      * Returns the grid's underlying element = used by panel.Grid
8768      * @return {Element} The element
8769      */
8770     getGridEl : function(){
8771         return this.el;
8772     },
8773      /**
8774      * Forces a resize - used by panel.Grid
8775      * @return {Element} The element
8776      */
8777     autoSize : function()
8778     {
8779         //var ctr = Roo.get(this.container.dom.parentElement);
8780         var ctr = Roo.get(this.el.dom);
8781         
8782         var thd = this.getGridEl().select('thead',true).first();
8783         var tbd = this.getGridEl().select('tbody', true).first();
8784         var tfd = this.getGridEl().select('tfoot', true).first();
8785         
8786         var cw = ctr.getWidth();
8787         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8788         
8789         if (tbd) {
8790             
8791             tbd.setWidth(ctr.getWidth());
8792             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8793             // this needs fixing for various usage - currently only hydra job advers I think..
8794             //tdb.setHeight(
8795             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8796             //); 
8797             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8798             cw -= barsize;
8799         }
8800         cw = Math.max(cw, this.totalWidth);
8801         this.getGridEl().select('tbody tr',true).setWidth(cw);
8802         
8803         // resize 'expandable coloumn?
8804         
8805         return; // we doe not have a view in this design..
8806         
8807     },
8808     onBodyScroll: function()
8809     {
8810         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8811         if(this.mainHead){
8812             this.mainHead.setStyle({
8813                 'position' : 'relative',
8814                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8815             });
8816         }
8817         
8818         if(this.lazyLoad){
8819             
8820             var scrollHeight = this.mainBody.dom.scrollHeight;
8821             
8822             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8823             
8824             var height = this.mainBody.getHeight();
8825             
8826             if(scrollHeight - height == scrollTop) {
8827                 
8828                 var total = this.ds.getTotalCount();
8829                 
8830                 if(this.footer.cursor + this.footer.pageSize < total){
8831                     
8832                     this.footer.ds.load({
8833                         params : {
8834                             start : this.footer.cursor + this.footer.pageSize,
8835                             limit : this.footer.pageSize
8836                         },
8837                         add : true
8838                     });
8839                 }
8840             }
8841             
8842         }
8843     },
8844     
8845     onHeaderChange : function()
8846     {
8847         var header = this.renderHeader();
8848         var table = this.el.select('table', true).first();
8849         
8850         this.mainHead.remove();
8851         this.mainHead = table.createChild(header, this.mainBody, false);
8852     },
8853     
8854     onHiddenChange : function(colModel, colIndex, hidden)
8855     {
8856         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8857         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8858         
8859         this.CSS.updateRule(thSelector, "display", "");
8860         this.CSS.updateRule(tdSelector, "display", "");
8861         
8862         if(hidden){
8863             this.CSS.updateRule(thSelector, "display", "none");
8864             this.CSS.updateRule(tdSelector, "display", "none");
8865         }
8866         
8867         this.onHeaderChange();
8868         this.onLoad();
8869     },
8870     
8871     setColumnWidth: function(col_index, width)
8872     {
8873         // width = "md-2 xs-2..."
8874         if(!this.colModel.config[col_index]) {
8875             return;
8876         }
8877         
8878         var w = width.split(" ");
8879         
8880         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8881         
8882         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8883         
8884         
8885         for(var j = 0; j < w.length; j++) {
8886             
8887             if(!w[j]) {
8888                 continue;
8889             }
8890             
8891             var size_cls = w[j].split("-");
8892             
8893             if(!Number.isInteger(size_cls[1] * 1)) {
8894                 continue;
8895             }
8896             
8897             if(!this.colModel.config[col_index][size_cls[0]]) {
8898                 continue;
8899             }
8900             
8901             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8902                 continue;
8903             }
8904             
8905             h_row[0].classList.replace(
8906                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8907                 "col-"+size_cls[0]+"-"+size_cls[1]
8908             );
8909             
8910             for(var i = 0; i < rows.length; i++) {
8911                 
8912                 var size_cls = w[j].split("-");
8913                 
8914                 if(!Number.isInteger(size_cls[1] * 1)) {
8915                     continue;
8916                 }
8917                 
8918                 if(!this.colModel.config[col_index][size_cls[0]]) {
8919                     continue;
8920                 }
8921                 
8922                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8923                     continue;
8924                 }
8925                 
8926                 rows[i].classList.replace(
8927                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8928                     "col-"+size_cls[0]+"-"+size_cls[1]
8929                 );
8930             }
8931             
8932             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8933         }
8934     }
8935 });
8936
8937  
8938
8939  /*
8940  * - LGPL
8941  *
8942  * table cell
8943  * 
8944  */
8945
8946 /**
8947  * @class Roo.bootstrap.TableCell
8948  * @extends Roo.bootstrap.Component
8949  * Bootstrap TableCell class
8950  * @cfg {String} html cell contain text
8951  * @cfg {String} cls cell class
8952  * @cfg {String} tag cell tag (td|th) default td
8953  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8954  * @cfg {String} align Aligns the content in a cell
8955  * @cfg {String} axis Categorizes cells
8956  * @cfg {String} bgcolor Specifies the background color of a cell
8957  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8958  * @cfg {Number} colspan Specifies the number of columns a cell should span
8959  * @cfg {String} headers Specifies one or more header cells a cell is related to
8960  * @cfg {Number} height Sets the height of a cell
8961  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8962  * @cfg {Number} rowspan Sets the number of rows a cell should span
8963  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8964  * @cfg {String} valign Vertical aligns the content in a cell
8965  * @cfg {Number} width Specifies the width of a cell
8966  * 
8967  * @constructor
8968  * Create a new TableCell
8969  * @param {Object} config The config object
8970  */
8971
8972 Roo.bootstrap.TableCell = function(config){
8973     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8974 };
8975
8976 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8977     
8978     html: false,
8979     cls: false,
8980     tag: false,
8981     abbr: false,
8982     align: false,
8983     axis: false,
8984     bgcolor: false,
8985     charoff: false,
8986     colspan: false,
8987     headers: false,
8988     height: false,
8989     nowrap: false,
8990     rowspan: false,
8991     scope: false,
8992     valign: false,
8993     width: false,
8994     
8995     
8996     getAutoCreate : function(){
8997         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8998         
8999         cfg = {
9000             tag: 'td'
9001         };
9002         
9003         if(this.tag){
9004             cfg.tag = this.tag;
9005         }
9006         
9007         if (this.html) {
9008             cfg.html=this.html
9009         }
9010         if (this.cls) {
9011             cfg.cls=this.cls
9012         }
9013         if (this.abbr) {
9014             cfg.abbr=this.abbr
9015         }
9016         if (this.align) {
9017             cfg.align=this.align
9018         }
9019         if (this.axis) {
9020             cfg.axis=this.axis
9021         }
9022         if (this.bgcolor) {
9023             cfg.bgcolor=this.bgcolor
9024         }
9025         if (this.charoff) {
9026             cfg.charoff=this.charoff
9027         }
9028         if (this.colspan) {
9029             cfg.colspan=this.colspan
9030         }
9031         if (this.headers) {
9032             cfg.headers=this.headers
9033         }
9034         if (this.height) {
9035             cfg.height=this.height
9036         }
9037         if (this.nowrap) {
9038             cfg.nowrap=this.nowrap
9039         }
9040         if (this.rowspan) {
9041             cfg.rowspan=this.rowspan
9042         }
9043         if (this.scope) {
9044             cfg.scope=this.scope
9045         }
9046         if (this.valign) {
9047             cfg.valign=this.valign
9048         }
9049         if (this.width) {
9050             cfg.width=this.width
9051         }
9052         
9053         
9054         return cfg;
9055     }
9056    
9057 });
9058
9059  
9060
9061  /*
9062  * - LGPL
9063  *
9064  * table row
9065  * 
9066  */
9067
9068 /**
9069  * @class Roo.bootstrap.TableRow
9070  * @extends Roo.bootstrap.Component
9071  * Bootstrap TableRow class
9072  * @cfg {String} cls row class
9073  * @cfg {String} align Aligns the content in a table row
9074  * @cfg {String} bgcolor Specifies a background color for a table row
9075  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9076  * @cfg {String} valign Vertical aligns the content in a table row
9077  * 
9078  * @constructor
9079  * Create a new TableRow
9080  * @param {Object} config The config object
9081  */
9082
9083 Roo.bootstrap.TableRow = function(config){
9084     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9085 };
9086
9087 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9088     
9089     cls: false,
9090     align: false,
9091     bgcolor: false,
9092     charoff: false,
9093     valign: false,
9094     
9095     getAutoCreate : function(){
9096         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9097         
9098         cfg = {
9099             tag: 'tr'
9100         };
9101             
9102         if(this.cls){
9103             cfg.cls = this.cls;
9104         }
9105         if(this.align){
9106             cfg.align = this.align;
9107         }
9108         if(this.bgcolor){
9109             cfg.bgcolor = this.bgcolor;
9110         }
9111         if(this.charoff){
9112             cfg.charoff = this.charoff;
9113         }
9114         if(this.valign){
9115             cfg.valign = this.valign;
9116         }
9117         
9118         return cfg;
9119     }
9120    
9121 });
9122
9123  
9124
9125  /*
9126  * - LGPL
9127  *
9128  * table body
9129  * 
9130  */
9131
9132 /**
9133  * @class Roo.bootstrap.TableBody
9134  * @extends Roo.bootstrap.Component
9135  * Bootstrap TableBody class
9136  * @cfg {String} cls element class
9137  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9138  * @cfg {String} align Aligns the content inside the element
9139  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9140  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9141  * 
9142  * @constructor
9143  * Create a new TableBody
9144  * @param {Object} config The config object
9145  */
9146
9147 Roo.bootstrap.TableBody = function(config){
9148     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9149 };
9150
9151 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9152     
9153     cls: false,
9154     tag: false,
9155     align: false,
9156     charoff: false,
9157     valign: false,
9158     
9159     getAutoCreate : function(){
9160         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9161         
9162         cfg = {
9163             tag: 'tbody'
9164         };
9165             
9166         if (this.cls) {
9167             cfg.cls=this.cls
9168         }
9169         if(this.tag){
9170             cfg.tag = this.tag;
9171         }
9172         
9173         if(this.align){
9174             cfg.align = this.align;
9175         }
9176         if(this.charoff){
9177             cfg.charoff = this.charoff;
9178         }
9179         if(this.valign){
9180             cfg.valign = this.valign;
9181         }
9182         
9183         return cfg;
9184     }
9185     
9186     
9187 //    initEvents : function()
9188 //    {
9189 //        
9190 //        if(!this.store){
9191 //            return;
9192 //        }
9193 //        
9194 //        this.store = Roo.factory(this.store, Roo.data);
9195 //        this.store.on('load', this.onLoad, this);
9196 //        
9197 //        this.store.load();
9198 //        
9199 //    },
9200 //    
9201 //    onLoad: function () 
9202 //    {   
9203 //        this.fireEvent('load', this);
9204 //    }
9205 //    
9206 //   
9207 });
9208
9209  
9210
9211  /*
9212  * Based on:
9213  * Ext JS Library 1.1.1
9214  * Copyright(c) 2006-2007, Ext JS, LLC.
9215  *
9216  * Originally Released Under LGPL - original licence link has changed is not relivant.
9217  *
9218  * Fork - LGPL
9219  * <script type="text/javascript">
9220  */
9221
9222 // as we use this in bootstrap.
9223 Roo.namespace('Roo.form');
9224  /**
9225  * @class Roo.form.Action
9226  * Internal Class used to handle form actions
9227  * @constructor
9228  * @param {Roo.form.BasicForm} el The form element or its id
9229  * @param {Object} config Configuration options
9230  */
9231
9232  
9233  
9234 // define the action interface
9235 Roo.form.Action = function(form, options){
9236     this.form = form;
9237     this.options = options || {};
9238 };
9239 /**
9240  * Client Validation Failed
9241  * @const 
9242  */
9243 Roo.form.Action.CLIENT_INVALID = 'client';
9244 /**
9245  * Server Validation Failed
9246  * @const 
9247  */
9248 Roo.form.Action.SERVER_INVALID = 'server';
9249  /**
9250  * Connect to Server Failed
9251  * @const 
9252  */
9253 Roo.form.Action.CONNECT_FAILURE = 'connect';
9254 /**
9255  * Reading Data from Server Failed
9256  * @const 
9257  */
9258 Roo.form.Action.LOAD_FAILURE = 'load';
9259
9260 Roo.form.Action.prototype = {
9261     type : 'default',
9262     failureType : undefined,
9263     response : undefined,
9264     result : undefined,
9265
9266     // interface method
9267     run : function(options){
9268
9269     },
9270
9271     // interface method
9272     success : function(response){
9273
9274     },
9275
9276     // interface method
9277     handleResponse : function(response){
9278
9279     },
9280
9281     // default connection failure
9282     failure : function(response){
9283         
9284         this.response = response;
9285         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9286         this.form.afterAction(this, false);
9287     },
9288
9289     processResponse : function(response){
9290         this.response = response;
9291         if(!response.responseText){
9292             return true;
9293         }
9294         this.result = this.handleResponse(response);
9295         return this.result;
9296     },
9297
9298     // utility functions used internally
9299     getUrl : function(appendParams){
9300         var url = this.options.url || this.form.url || this.form.el.dom.action;
9301         if(appendParams){
9302             var p = this.getParams();
9303             if(p){
9304                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9305             }
9306         }
9307         return url;
9308     },
9309
9310     getMethod : function(){
9311         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9312     },
9313
9314     getParams : function(){
9315         var bp = this.form.baseParams;
9316         var p = this.options.params;
9317         if(p){
9318             if(typeof p == "object"){
9319                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9320             }else if(typeof p == 'string' && bp){
9321                 p += '&' + Roo.urlEncode(bp);
9322             }
9323         }else if(bp){
9324             p = Roo.urlEncode(bp);
9325         }
9326         return p;
9327     },
9328
9329     createCallback : function(){
9330         return {
9331             success: this.success,
9332             failure: this.failure,
9333             scope: this,
9334             timeout: (this.form.timeout*1000),
9335             upload: this.form.fileUpload ? this.success : undefined
9336         };
9337     }
9338 };
9339
9340 Roo.form.Action.Submit = function(form, options){
9341     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9342 };
9343
9344 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9345     type : 'submit',
9346
9347     haveProgress : false,
9348     uploadComplete : false,
9349     
9350     // uploadProgress indicator.
9351     uploadProgress : function()
9352     {
9353         if (!this.form.progressUrl) {
9354             return;
9355         }
9356         
9357         if (!this.haveProgress) {
9358             Roo.MessageBox.progress("Uploading", "Uploading");
9359         }
9360         if (this.uploadComplete) {
9361            Roo.MessageBox.hide();
9362            return;
9363         }
9364         
9365         this.haveProgress = true;
9366    
9367         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9368         
9369         var c = new Roo.data.Connection();
9370         c.request({
9371             url : this.form.progressUrl,
9372             params: {
9373                 id : uid
9374             },
9375             method: 'GET',
9376             success : function(req){
9377                //console.log(data);
9378                 var rdata = false;
9379                 var edata;
9380                 try  {
9381                    rdata = Roo.decode(req.responseText)
9382                 } catch (e) {
9383                     Roo.log("Invalid data from server..");
9384                     Roo.log(edata);
9385                     return;
9386                 }
9387                 if (!rdata || !rdata.success) {
9388                     Roo.log(rdata);
9389                     Roo.MessageBox.alert(Roo.encode(rdata));
9390                     return;
9391                 }
9392                 var data = rdata.data;
9393                 
9394                 if (this.uploadComplete) {
9395                    Roo.MessageBox.hide();
9396                    return;
9397                 }
9398                    
9399                 if (data){
9400                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9401                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9402                     );
9403                 }
9404                 this.uploadProgress.defer(2000,this);
9405             },
9406        
9407             failure: function(data) {
9408                 Roo.log('progress url failed ');
9409                 Roo.log(data);
9410             },
9411             scope : this
9412         });
9413            
9414     },
9415     
9416     
9417     run : function()
9418     {
9419         // run get Values on the form, so it syncs any secondary forms.
9420         this.form.getValues();
9421         
9422         var o = this.options;
9423         var method = this.getMethod();
9424         var isPost = method == 'POST';
9425         if(o.clientValidation === false || this.form.isValid()){
9426             
9427             if (this.form.progressUrl) {
9428                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9429                     (new Date() * 1) + '' + Math.random());
9430                     
9431             } 
9432             
9433             
9434             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9435                 form:this.form.el.dom,
9436                 url:this.getUrl(!isPost),
9437                 method: method,
9438                 params:isPost ? this.getParams() : null,
9439                 isUpload: this.form.fileUpload,
9440                 formData : this.form.formData
9441             }));
9442             
9443             this.uploadProgress();
9444
9445         }else if (o.clientValidation !== false){ // client validation failed
9446             this.failureType = Roo.form.Action.CLIENT_INVALID;
9447             this.form.afterAction(this, false);
9448         }
9449     },
9450
9451     success : function(response)
9452     {
9453         this.uploadComplete= true;
9454         if (this.haveProgress) {
9455             Roo.MessageBox.hide();
9456         }
9457         
9458         
9459         var result = this.processResponse(response);
9460         if(result === true || result.success){
9461             this.form.afterAction(this, true);
9462             return;
9463         }
9464         if(result.errors){
9465             this.form.markInvalid(result.errors);
9466             this.failureType = Roo.form.Action.SERVER_INVALID;
9467         }
9468         this.form.afterAction(this, false);
9469     },
9470     failure : function(response)
9471     {
9472         this.uploadComplete= true;
9473         if (this.haveProgress) {
9474             Roo.MessageBox.hide();
9475         }
9476         
9477         this.response = response;
9478         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9479         this.form.afterAction(this, false);
9480     },
9481     
9482     handleResponse : function(response){
9483         if(this.form.errorReader){
9484             var rs = this.form.errorReader.read(response);
9485             var errors = [];
9486             if(rs.records){
9487                 for(var i = 0, len = rs.records.length; i < len; i++) {
9488                     var r = rs.records[i];
9489                     errors[i] = r.data;
9490                 }
9491             }
9492             if(errors.length < 1){
9493                 errors = null;
9494             }
9495             return {
9496                 success : rs.success,
9497                 errors : errors
9498             };
9499         }
9500         var ret = false;
9501         try {
9502             ret = Roo.decode(response.responseText);
9503         } catch (e) {
9504             ret = {
9505                 success: false,
9506                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9507                 errors : []
9508             };
9509         }
9510         return ret;
9511         
9512     }
9513 });
9514
9515
9516 Roo.form.Action.Load = function(form, options){
9517     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9518     this.reader = this.form.reader;
9519 };
9520
9521 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9522     type : 'load',
9523
9524     run : function(){
9525         
9526         Roo.Ajax.request(Roo.apply(
9527                 this.createCallback(), {
9528                     method:this.getMethod(),
9529                     url:this.getUrl(false),
9530                     params:this.getParams()
9531         }));
9532     },
9533
9534     success : function(response){
9535         
9536         var result = this.processResponse(response);
9537         if(result === true || !result.success || !result.data){
9538             this.failureType = Roo.form.Action.LOAD_FAILURE;
9539             this.form.afterAction(this, false);
9540             return;
9541         }
9542         this.form.clearInvalid();
9543         this.form.setValues(result.data);
9544         this.form.afterAction(this, true);
9545     },
9546
9547     handleResponse : function(response){
9548         if(this.form.reader){
9549             var rs = this.form.reader.read(response);
9550             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9551             return {
9552                 success : rs.success,
9553                 data : data
9554             };
9555         }
9556         return Roo.decode(response.responseText);
9557     }
9558 });
9559
9560 Roo.form.Action.ACTION_TYPES = {
9561     'load' : Roo.form.Action.Load,
9562     'submit' : Roo.form.Action.Submit
9563 };/*
9564  * - LGPL
9565  *
9566  * form
9567  *
9568  */
9569
9570 /**
9571  * @class Roo.bootstrap.Form
9572  * @extends Roo.bootstrap.Component
9573  * Bootstrap Form class
9574  * @cfg {String} method  GET | POST (default POST)
9575  * @cfg {String} labelAlign top | left (default top)
9576  * @cfg {String} align left  | right - for navbars
9577  * @cfg {Boolean} loadMask load mask when submit (default true)
9578
9579  *
9580  * @constructor
9581  * Create a new Form
9582  * @param {Object} config The config object
9583  */
9584
9585
9586 Roo.bootstrap.Form = function(config){
9587     
9588     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9589     
9590     Roo.bootstrap.Form.popover.apply();
9591     
9592     this.addEvents({
9593         /**
9594          * @event clientvalidation
9595          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9596          * @param {Form} this
9597          * @param {Boolean} valid true if the form has passed client-side validation
9598          */
9599         clientvalidation: true,
9600         /**
9601          * @event beforeaction
9602          * Fires before any action is performed. Return false to cancel the action.
9603          * @param {Form} this
9604          * @param {Action} action The action to be performed
9605          */
9606         beforeaction: true,
9607         /**
9608          * @event actionfailed
9609          * Fires when an action fails.
9610          * @param {Form} this
9611          * @param {Action} action The action that failed
9612          */
9613         actionfailed : true,
9614         /**
9615          * @event actioncomplete
9616          * Fires when an action is completed.
9617          * @param {Form} this
9618          * @param {Action} action The action that completed
9619          */
9620         actioncomplete : true
9621     });
9622 };
9623
9624 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9625
9626      /**
9627      * @cfg {String} method
9628      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9629      */
9630     method : 'POST',
9631     /**
9632      * @cfg {String} url
9633      * The URL to use for form actions if one isn't supplied in the action options.
9634      */
9635     /**
9636      * @cfg {Boolean} fileUpload
9637      * Set to true if this form is a file upload.
9638      */
9639
9640     /**
9641      * @cfg {Object} baseParams
9642      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9643      */
9644
9645     /**
9646      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9647      */
9648     timeout: 30,
9649     /**
9650      * @cfg {Sting} align (left|right) for navbar forms
9651      */
9652     align : 'left',
9653
9654     // private
9655     activeAction : null,
9656
9657     /**
9658      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9659      * element by passing it or its id or mask the form itself by passing in true.
9660      * @type Mixed
9661      */
9662     waitMsgTarget : false,
9663
9664     loadMask : true,
9665     
9666     /**
9667      * @cfg {Boolean} errorMask (true|false) default false
9668      */
9669     errorMask : false,
9670     
9671     /**
9672      * @cfg {Number} maskOffset Default 100
9673      */
9674     maskOffset : 100,
9675     
9676     /**
9677      * @cfg {Boolean} maskBody
9678      */
9679     maskBody : false,
9680
9681     getAutoCreate : function(){
9682
9683         var cfg = {
9684             tag: 'form',
9685             method : this.method || 'POST',
9686             id : this.id || Roo.id(),
9687             cls : ''
9688         };
9689         if (this.parent().xtype.match(/^Nav/)) {
9690             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9691
9692         }
9693
9694         if (this.labelAlign == 'left' ) {
9695             cfg.cls += ' form-horizontal';
9696         }
9697
9698
9699         return cfg;
9700     },
9701     initEvents : function()
9702     {
9703         this.el.on('submit', this.onSubmit, this);
9704         // this was added as random key presses on the form where triggering form submit.
9705         this.el.on('keypress', function(e) {
9706             if (e.getCharCode() != 13) {
9707                 return true;
9708             }
9709             // we might need to allow it for textareas.. and some other items.
9710             // check e.getTarget().
9711
9712             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9713                 return true;
9714             }
9715
9716             Roo.log("keypress blocked");
9717
9718             e.preventDefault();
9719             return false;
9720         });
9721         
9722     },
9723     // private
9724     onSubmit : function(e){
9725         e.stopEvent();
9726     },
9727
9728      /**
9729      * Returns true if client-side validation on the form is successful.
9730      * @return Boolean
9731      */
9732     isValid : function(){
9733         var items = this.getItems();
9734         var valid = true;
9735         var target = false;
9736         
9737         items.each(function(f){
9738             
9739             if(f.validate()){
9740                 return;
9741             }
9742             
9743             Roo.log('invalid field: ' + f.name);
9744             
9745             valid = false;
9746
9747             if(!target && f.el.isVisible(true)){
9748                 target = f;
9749             }
9750            
9751         });
9752         
9753         if(this.errorMask && !valid){
9754             Roo.bootstrap.Form.popover.mask(this, target);
9755         }
9756         
9757         return valid;
9758     },
9759     
9760     /**
9761      * Returns true if any fields in this form have changed since their original load.
9762      * @return Boolean
9763      */
9764     isDirty : function(){
9765         var dirty = false;
9766         var items = this.getItems();
9767         items.each(function(f){
9768            if(f.isDirty()){
9769                dirty = true;
9770                return false;
9771            }
9772            return true;
9773         });
9774         return dirty;
9775     },
9776      /**
9777      * Performs a predefined action (submit or load) or custom actions you define on this form.
9778      * @param {String} actionName The name of the action type
9779      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9780      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9781      * accept other config options):
9782      * <pre>
9783 Property          Type             Description
9784 ----------------  ---------------  ----------------------------------------------------------------------------------
9785 url               String           The url for the action (defaults to the form's url)
9786 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9787 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9788 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9789                                    validate the form on the client (defaults to false)
9790      * </pre>
9791      * @return {BasicForm} this
9792      */
9793     doAction : function(action, options){
9794         if(typeof action == 'string'){
9795             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9796         }
9797         if(this.fireEvent('beforeaction', this, action) !== false){
9798             this.beforeAction(action);
9799             action.run.defer(100, action);
9800         }
9801         return this;
9802     },
9803
9804     // private
9805     beforeAction : function(action){
9806         var o = action.options;
9807         
9808         if(this.loadMask){
9809             
9810             if(this.maskBody){
9811                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9812             } else {
9813                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9814             }
9815         }
9816         // not really supported yet.. ??
9817
9818         //if(this.waitMsgTarget === true){
9819         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9820         //}else if(this.waitMsgTarget){
9821         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9822         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9823         //}else {
9824         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9825        // }
9826
9827     },
9828
9829     // private
9830     afterAction : function(action, success){
9831         this.activeAction = null;
9832         var o = action.options;
9833
9834         if(this.loadMask){
9835             
9836             if(this.maskBody){
9837                 Roo.get(document.body).unmask();
9838             } else {
9839                 this.el.unmask();
9840             }
9841         }
9842         
9843         //if(this.waitMsgTarget === true){
9844 //            this.el.unmask();
9845         //}else if(this.waitMsgTarget){
9846         //    this.waitMsgTarget.unmask();
9847         //}else{
9848         //    Roo.MessageBox.updateProgress(1);
9849         //    Roo.MessageBox.hide();
9850        // }
9851         //
9852         if(success){
9853             if(o.reset){
9854                 this.reset();
9855             }
9856             Roo.callback(o.success, o.scope, [this, action]);
9857             this.fireEvent('actioncomplete', this, action);
9858
9859         }else{
9860
9861             // failure condition..
9862             // we have a scenario where updates need confirming.
9863             // eg. if a locking scenario exists..
9864             // we look for { errors : { needs_confirm : true }} in the response.
9865             if (
9866                 (typeof(action.result) != 'undefined')  &&
9867                 (typeof(action.result.errors) != 'undefined')  &&
9868                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9869            ){
9870                 var _t = this;
9871                 Roo.log("not supported yet");
9872                  /*
9873
9874                 Roo.MessageBox.confirm(
9875                     "Change requires confirmation",
9876                     action.result.errorMsg,
9877                     function(r) {
9878                         if (r != 'yes') {
9879                             return;
9880                         }
9881                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9882                     }
9883
9884                 );
9885                 */
9886
9887
9888                 return;
9889             }
9890
9891             Roo.callback(o.failure, o.scope, [this, action]);
9892             // show an error message if no failed handler is set..
9893             if (!this.hasListener('actionfailed')) {
9894                 Roo.log("need to add dialog support");
9895                 /*
9896                 Roo.MessageBox.alert("Error",
9897                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9898                         action.result.errorMsg :
9899                         "Saving Failed, please check your entries or try again"
9900                 );
9901                 */
9902             }
9903
9904             this.fireEvent('actionfailed', this, action);
9905         }
9906
9907     },
9908     /**
9909      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9910      * @param {String} id The value to search for
9911      * @return Field
9912      */
9913     findField : function(id){
9914         var items = this.getItems();
9915         var field = items.get(id);
9916         if(!field){
9917              items.each(function(f){
9918                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9919                     field = f;
9920                     return false;
9921                 }
9922                 return true;
9923             });
9924         }
9925         return field || null;
9926     },
9927      /**
9928      * Mark fields in this form invalid in bulk.
9929      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9930      * @return {BasicForm} this
9931      */
9932     markInvalid : function(errors){
9933         if(errors instanceof Array){
9934             for(var i = 0, len = errors.length; i < len; i++){
9935                 var fieldError = errors[i];
9936                 var f = this.findField(fieldError.id);
9937                 if(f){
9938                     f.markInvalid(fieldError.msg);
9939                 }
9940             }
9941         }else{
9942             var field, id;
9943             for(id in errors){
9944                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9945                     field.markInvalid(errors[id]);
9946                 }
9947             }
9948         }
9949         //Roo.each(this.childForms || [], function (f) {
9950         //    f.markInvalid(errors);
9951         //});
9952
9953         return this;
9954     },
9955
9956     /**
9957      * Set values for fields in this form in bulk.
9958      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9959      * @return {BasicForm} this
9960      */
9961     setValues : function(values){
9962         if(values instanceof Array){ // array of objects
9963             for(var i = 0, len = values.length; i < len; i++){
9964                 var v = values[i];
9965                 var f = this.findField(v.id);
9966                 if(f){
9967                     f.setValue(v.value);
9968                     if(this.trackResetOnLoad){
9969                         f.originalValue = f.getValue();
9970                     }
9971                 }
9972             }
9973         }else{ // object hash
9974             var field, id;
9975             for(id in values){
9976                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9977
9978                     if (field.setFromData &&
9979                         field.valueField &&
9980                         field.displayField &&
9981                         // combos' with local stores can
9982                         // be queried via setValue()
9983                         // to set their value..
9984                         (field.store && !field.store.isLocal)
9985                         ) {
9986                         // it's a combo
9987                         var sd = { };
9988                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9989                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9990                         field.setFromData(sd);
9991
9992                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9993                         
9994                         field.setFromData(values);
9995                         
9996                     } else {
9997                         field.setValue(values[id]);
9998                     }
9999
10000
10001                     if(this.trackResetOnLoad){
10002                         field.originalValue = field.getValue();
10003                     }
10004                 }
10005             }
10006         }
10007
10008         //Roo.each(this.childForms || [], function (f) {
10009         //    f.setValues(values);
10010         //});
10011
10012         return this;
10013     },
10014
10015     /**
10016      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10017      * they are returned as an array.
10018      * @param {Boolean} asString
10019      * @return {Object}
10020      */
10021     getValues : function(asString){
10022         //if (this.childForms) {
10023             // copy values from the child forms
10024         //    Roo.each(this.childForms, function (f) {
10025         //        this.setValues(f.getValues());
10026         //    }, this);
10027         //}
10028
10029
10030
10031         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10032         if(asString === true){
10033             return fs;
10034         }
10035         return Roo.urlDecode(fs);
10036     },
10037
10038     /**
10039      * Returns the fields in this form as an object with key/value pairs.
10040      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10041      * @return {Object}
10042      */
10043     getFieldValues : function(with_hidden)
10044     {
10045         var items = this.getItems();
10046         var ret = {};
10047         items.each(function(f){
10048             
10049             if (!f.getName()) {
10050                 return;
10051             }
10052             
10053             var v = f.getValue();
10054             
10055             if (f.inputType =='radio') {
10056                 if (typeof(ret[f.getName()]) == 'undefined') {
10057                     ret[f.getName()] = ''; // empty..
10058                 }
10059
10060                 if (!f.el.dom.checked) {
10061                     return;
10062
10063                 }
10064                 v = f.el.dom.value;
10065
10066             }
10067             
10068             if(f.xtype == 'MoneyField'){
10069                 ret[f.currencyName] = f.getCurrency();
10070             }
10071
10072             // not sure if this supported any more..
10073             if ((typeof(v) == 'object') && f.getRawValue) {
10074                 v = f.getRawValue() ; // dates..
10075             }
10076             // combo boxes where name != hiddenName...
10077             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10078                 ret[f.name] = f.getRawValue();
10079             }
10080             ret[f.getName()] = v;
10081         });
10082
10083         return ret;
10084     },
10085
10086     /**
10087      * Clears all invalid messages in this form.
10088      * @return {BasicForm} this
10089      */
10090     clearInvalid : function(){
10091         var items = this.getItems();
10092
10093         items.each(function(f){
10094            f.clearInvalid();
10095         });
10096
10097         return this;
10098     },
10099
10100     /**
10101      * Resets this form.
10102      * @return {BasicForm} this
10103      */
10104     reset : function(){
10105         var items = this.getItems();
10106         items.each(function(f){
10107             f.reset();
10108         });
10109
10110         Roo.each(this.childForms || [], function (f) {
10111             f.reset();
10112         });
10113
10114
10115         return this;
10116     },
10117     
10118     getItems : function()
10119     {
10120         var r=new Roo.util.MixedCollection(false, function(o){
10121             return o.id || (o.id = Roo.id());
10122         });
10123         var iter = function(el) {
10124             if (el.inputEl) {
10125                 r.add(el);
10126             }
10127             if (!el.items) {
10128                 return;
10129             }
10130             Roo.each(el.items,function(e) {
10131                 iter(e);
10132             });
10133         };
10134
10135         iter(this);
10136         return r;
10137     },
10138     
10139     hideFields : function(items)
10140     {
10141         Roo.each(items, function(i){
10142             
10143             var f = this.findField(i);
10144             
10145             if(!f){
10146                 return;
10147             }
10148             
10149             f.hide();
10150             
10151         }, this);
10152     },
10153     
10154     showFields : function(items)
10155     {
10156         Roo.each(items, function(i){
10157             
10158             var f = this.findField(i);
10159             
10160             if(!f){
10161                 return;
10162             }
10163             
10164             f.show();
10165             
10166         }, this);
10167     }
10168
10169 });
10170
10171 Roo.apply(Roo.bootstrap.Form, {
10172     
10173     popover : {
10174         
10175         padding : 5,
10176         
10177         isApplied : false,
10178         
10179         isMasked : false,
10180         
10181         form : false,
10182         
10183         target : false,
10184         
10185         toolTip : false,
10186         
10187         intervalID : false,
10188         
10189         maskEl : false,
10190         
10191         apply : function()
10192         {
10193             if(this.isApplied){
10194                 return;
10195             }
10196             
10197             this.maskEl = {
10198                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10199                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10200                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10201                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10202             };
10203             
10204             this.maskEl.top.enableDisplayMode("block");
10205             this.maskEl.left.enableDisplayMode("block");
10206             this.maskEl.bottom.enableDisplayMode("block");
10207             this.maskEl.right.enableDisplayMode("block");
10208             
10209             this.toolTip = new Roo.bootstrap.Tooltip({
10210                 cls : 'roo-form-error-popover',
10211                 alignment : {
10212                     'left' : ['r-l', [-2,0], 'right'],
10213                     'right' : ['l-r', [2,0], 'left'],
10214                     'bottom' : ['tl-bl', [0,2], 'top'],
10215                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10216                 }
10217             });
10218             
10219             this.toolTip.render(Roo.get(document.body));
10220
10221             this.toolTip.el.enableDisplayMode("block");
10222             
10223             Roo.get(document.body).on('click', function(){
10224                 this.unmask();
10225             }, this);
10226             
10227             Roo.get(document.body).on('touchstart', function(){
10228                 this.unmask();
10229             }, this);
10230             
10231             this.isApplied = true
10232         },
10233         
10234         mask : function(form, target)
10235         {
10236             this.form = form;
10237             
10238             this.target = target;
10239             
10240             if(!this.form.errorMask || !target.el){
10241                 return;
10242             }
10243             
10244             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10245             
10246             Roo.log(scrollable);
10247             
10248             var ot = this.target.el.calcOffsetsTo(scrollable);
10249             
10250             var scrollTo = ot[1] - this.form.maskOffset;
10251             
10252             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10253             
10254             scrollable.scrollTo('top', scrollTo);
10255             
10256             var box = this.target.el.getBox();
10257             Roo.log(box);
10258             var zIndex = Roo.bootstrap.Modal.zIndex++;
10259
10260             
10261             this.maskEl.top.setStyle('position', 'absolute');
10262             this.maskEl.top.setStyle('z-index', zIndex);
10263             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10264             this.maskEl.top.setLeft(0);
10265             this.maskEl.top.setTop(0);
10266             this.maskEl.top.show();
10267             
10268             this.maskEl.left.setStyle('position', 'absolute');
10269             this.maskEl.left.setStyle('z-index', zIndex);
10270             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10271             this.maskEl.left.setLeft(0);
10272             this.maskEl.left.setTop(box.y - this.padding);
10273             this.maskEl.left.show();
10274
10275             this.maskEl.bottom.setStyle('position', 'absolute');
10276             this.maskEl.bottom.setStyle('z-index', zIndex);
10277             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10278             this.maskEl.bottom.setLeft(0);
10279             this.maskEl.bottom.setTop(box.bottom + this.padding);
10280             this.maskEl.bottom.show();
10281
10282             this.maskEl.right.setStyle('position', 'absolute');
10283             this.maskEl.right.setStyle('z-index', zIndex);
10284             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10285             this.maskEl.right.setLeft(box.right + this.padding);
10286             this.maskEl.right.setTop(box.y - this.padding);
10287             this.maskEl.right.show();
10288
10289             this.toolTip.bindEl = this.target.el;
10290
10291             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10292
10293             var tip = this.target.blankText;
10294
10295             if(this.target.getValue() !== '' ) {
10296                 
10297                 if (this.target.invalidText.length) {
10298                     tip = this.target.invalidText;
10299                 } else if (this.target.regexText.length){
10300                     tip = this.target.regexText;
10301                 }
10302             }
10303
10304             this.toolTip.show(tip);
10305
10306             this.intervalID = window.setInterval(function() {
10307                 Roo.bootstrap.Form.popover.unmask();
10308             }, 10000);
10309
10310             window.onwheel = function(){ return false;};
10311             
10312             (function(){ this.isMasked = true; }).defer(500, this);
10313             
10314         },
10315         
10316         unmask : function()
10317         {
10318             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10319                 return;
10320             }
10321             
10322             this.maskEl.top.setStyle('position', 'absolute');
10323             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10324             this.maskEl.top.hide();
10325
10326             this.maskEl.left.setStyle('position', 'absolute');
10327             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10328             this.maskEl.left.hide();
10329
10330             this.maskEl.bottom.setStyle('position', 'absolute');
10331             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10332             this.maskEl.bottom.hide();
10333
10334             this.maskEl.right.setStyle('position', 'absolute');
10335             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10336             this.maskEl.right.hide();
10337             
10338             this.toolTip.hide();
10339             
10340             this.toolTip.el.hide();
10341             
10342             window.onwheel = function(){ return true;};
10343             
10344             if(this.intervalID){
10345                 window.clearInterval(this.intervalID);
10346                 this.intervalID = false;
10347             }
10348             
10349             this.isMasked = false;
10350             
10351         }
10352         
10353     }
10354     
10355 });
10356
10357 /*
10358  * Based on:
10359  * Ext JS Library 1.1.1
10360  * Copyright(c) 2006-2007, Ext JS, LLC.
10361  *
10362  * Originally Released Under LGPL - original licence link has changed is not relivant.
10363  *
10364  * Fork - LGPL
10365  * <script type="text/javascript">
10366  */
10367 /**
10368  * @class Roo.form.VTypes
10369  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10370  * @singleton
10371  */
10372 Roo.form.VTypes = function(){
10373     // closure these in so they are only created once.
10374     var alpha = /^[a-zA-Z_]+$/;
10375     var alphanum = /^[a-zA-Z0-9_]+$/;
10376     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10377     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10378
10379     // All these messages and functions are configurable
10380     return {
10381         /**
10382          * The function used to validate email addresses
10383          * @param {String} value The email address
10384          */
10385         'email' : function(v){
10386             return email.test(v);
10387         },
10388         /**
10389          * The error text to display when the email validation function returns false
10390          * @type String
10391          */
10392         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10393         /**
10394          * The keystroke filter mask to be applied on email input
10395          * @type RegExp
10396          */
10397         'emailMask' : /[a-z0-9_\.\-@]/i,
10398
10399         /**
10400          * The function used to validate URLs
10401          * @param {String} value The URL
10402          */
10403         'url' : function(v){
10404             return url.test(v);
10405         },
10406         /**
10407          * The error text to display when the url validation function returns false
10408          * @type String
10409          */
10410         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10411         
10412         /**
10413          * The function used to validate alpha values
10414          * @param {String} value The value
10415          */
10416         'alpha' : function(v){
10417             return alpha.test(v);
10418         },
10419         /**
10420          * The error text to display when the alpha validation function returns false
10421          * @type String
10422          */
10423         'alphaText' : 'This field should only contain letters and _',
10424         /**
10425          * The keystroke filter mask to be applied on alpha input
10426          * @type RegExp
10427          */
10428         'alphaMask' : /[a-z_]/i,
10429
10430         /**
10431          * The function used to validate alphanumeric values
10432          * @param {String} value The value
10433          */
10434         'alphanum' : function(v){
10435             return alphanum.test(v);
10436         },
10437         /**
10438          * The error text to display when the alphanumeric validation function returns false
10439          * @type String
10440          */
10441         'alphanumText' : 'This field should only contain letters, numbers and _',
10442         /**
10443          * The keystroke filter mask to be applied on alphanumeric input
10444          * @type RegExp
10445          */
10446         'alphanumMask' : /[a-z0-9_]/i
10447     };
10448 }();/*
10449  * - LGPL
10450  *
10451  * Input
10452  * 
10453  */
10454
10455 /**
10456  * @class Roo.bootstrap.Input
10457  * @extends Roo.bootstrap.Component
10458  * Bootstrap Input class
10459  * @cfg {Boolean} disabled is it disabled
10460  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10461  * @cfg {String} name name of the input
10462  * @cfg {string} fieldLabel - the label associated
10463  * @cfg {string} placeholder - placeholder to put in text.
10464  * @cfg {string}  before - input group add on before
10465  * @cfg {string} after - input group add on after
10466  * @cfg {string} size - (lg|sm) or leave empty..
10467  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10468  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10469  * @cfg {Number} md colspan out of 12 for computer-sized screens
10470  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10471  * @cfg {string} value default value of the input
10472  * @cfg {Number} labelWidth set the width of label 
10473  * @cfg {Number} labellg set the width of label (1-12)
10474  * @cfg {Number} labelmd set the width of label (1-12)
10475  * @cfg {Number} labelsm set the width of label (1-12)
10476  * @cfg {Number} labelxs set the width of label (1-12)
10477  * @cfg {String} labelAlign (top|left)
10478  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10479  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10480  * @cfg {String} indicatorpos (left|right) default left
10481  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10482  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10483  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10484
10485  * @cfg {String} align (left|center|right) Default left
10486  * @cfg {Boolean} forceFeedback (true|false) Default false
10487  * 
10488  * @constructor
10489  * Create a new Input
10490  * @param {Object} config The config object
10491  */
10492
10493 Roo.bootstrap.Input = function(config){
10494     
10495     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10496     
10497     this.addEvents({
10498         /**
10499          * @event focus
10500          * Fires when this field receives input focus.
10501          * @param {Roo.form.Field} this
10502          */
10503         focus : true,
10504         /**
10505          * @event blur
10506          * Fires when this field loses input focus.
10507          * @param {Roo.form.Field} this
10508          */
10509         blur : true,
10510         /**
10511          * @event specialkey
10512          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10513          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10514          * @param {Roo.form.Field} this
10515          * @param {Roo.EventObject} e The event object
10516          */
10517         specialkey : true,
10518         /**
10519          * @event change
10520          * Fires just before the field blurs if the field value has changed.
10521          * @param {Roo.form.Field} this
10522          * @param {Mixed} newValue The new value
10523          * @param {Mixed} oldValue The original value
10524          */
10525         change : true,
10526         /**
10527          * @event invalid
10528          * Fires after the field has been marked as invalid.
10529          * @param {Roo.form.Field} this
10530          * @param {String} msg The validation message
10531          */
10532         invalid : true,
10533         /**
10534          * @event valid
10535          * Fires after the field has been validated with no errors.
10536          * @param {Roo.form.Field} this
10537          */
10538         valid : true,
10539          /**
10540          * @event keyup
10541          * Fires after the key up
10542          * @param {Roo.form.Field} this
10543          * @param {Roo.EventObject}  e The event Object
10544          */
10545         keyup : true,
10546         /**
10547          * @event paste
10548          * Fires after the user pastes into input
10549          * @param {Roo.form.Field} this
10550          * @param {Roo.EventObject}  e The event Object
10551          */
10552         paste : true
10553     });
10554 };
10555
10556 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10557      /**
10558      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10559       automatic validation (defaults to "keyup").
10560      */
10561     validationEvent : "keyup",
10562      /**
10563      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10564      */
10565     validateOnBlur : true,
10566     /**
10567      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10568      */
10569     validationDelay : 250,
10570      /**
10571      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10572      */
10573     focusClass : "x-form-focus",  // not needed???
10574     
10575        
10576     /**
10577      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10578      */
10579     invalidClass : "has-warning",
10580     
10581     /**
10582      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10583      */
10584     validClass : "has-success",
10585     
10586     /**
10587      * @cfg {Boolean} hasFeedback (true|false) default true
10588      */
10589     hasFeedback : true,
10590     
10591     /**
10592      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10593      */
10594     invalidFeedbackClass : "glyphicon-warning-sign",
10595     
10596     /**
10597      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10598      */
10599     validFeedbackClass : "glyphicon-ok",
10600     
10601     /**
10602      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10603      */
10604     selectOnFocus : false,
10605     
10606      /**
10607      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10608      */
10609     maskRe : null,
10610        /**
10611      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10612      */
10613     vtype : null,
10614     
10615       /**
10616      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10617      */
10618     disableKeyFilter : false,
10619     
10620        /**
10621      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10622      */
10623     disabled : false,
10624      /**
10625      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10626      */
10627     allowBlank : true,
10628     /**
10629      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10630      */
10631     blankText : "Please complete this mandatory field",
10632     
10633      /**
10634      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10635      */
10636     minLength : 0,
10637     /**
10638      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10639      */
10640     maxLength : Number.MAX_VALUE,
10641     /**
10642      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10643      */
10644     minLengthText : "The minimum length for this field is {0}",
10645     /**
10646      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10647      */
10648     maxLengthText : "The maximum length for this field is {0}",
10649   
10650     
10651     /**
10652      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10653      * If available, this function will be called only after the basic validators all return true, and will be passed the
10654      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10655      */
10656     validator : null,
10657     /**
10658      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10659      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10660      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10661      */
10662     regex : null,
10663     /**
10664      * @cfg {String} regexText -- Depricated - use Invalid Text
10665      */
10666     regexText : "",
10667     
10668     /**
10669      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10670      */
10671     invalidText : "",
10672     
10673     
10674     
10675     autocomplete: false,
10676     
10677     
10678     fieldLabel : '',
10679     inputType : 'text',
10680     
10681     name : false,
10682     placeholder: false,
10683     before : false,
10684     after : false,
10685     size : false,
10686     hasFocus : false,
10687     preventMark: false,
10688     isFormField : true,
10689     value : '',
10690     labelWidth : 2,
10691     labelAlign : false,
10692     readOnly : false,
10693     align : false,
10694     formatedValue : false,
10695     forceFeedback : false,
10696     
10697     indicatorpos : 'left',
10698     
10699     labellg : 0,
10700     labelmd : 0,
10701     labelsm : 0,
10702     labelxs : 0,
10703     
10704     capture : '',
10705     accept : '',
10706     
10707     parentLabelAlign : function()
10708     {
10709         var parent = this;
10710         while (parent.parent()) {
10711             parent = parent.parent();
10712             if (typeof(parent.labelAlign) !='undefined') {
10713                 return parent.labelAlign;
10714             }
10715         }
10716         return 'left';
10717         
10718     },
10719     
10720     getAutoCreate : function()
10721     {
10722         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10723         
10724         var id = Roo.id();
10725         
10726         var cfg = {};
10727         
10728         if(this.inputType != 'hidden'){
10729             cfg.cls = 'form-group' //input-group
10730         }
10731         
10732         var input =  {
10733             tag: 'input',
10734             id : id,
10735             type : this.inputType,
10736             value : this.value,
10737             cls : 'form-control',
10738             placeholder : this.placeholder || '',
10739             autocomplete : this.autocomplete || 'new-password'
10740         };
10741         if (this.inputType == 'file') {
10742             input.style = 'overflow:hidden'; // why not in CSS?
10743         }
10744         
10745         if(this.capture.length){
10746             input.capture = this.capture;
10747         }
10748         
10749         if(this.accept.length){
10750             input.accept = this.accept + "/*";
10751         }
10752         
10753         if(this.align){
10754             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10755         }
10756         
10757         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10758             input.maxLength = this.maxLength;
10759         }
10760         
10761         if (this.disabled) {
10762             input.disabled=true;
10763         }
10764         
10765         if (this.readOnly) {
10766             input.readonly=true;
10767         }
10768         
10769         if (this.name) {
10770             input.name = this.name;
10771         }
10772         
10773         if (this.size) {
10774             input.cls += ' input-' + this.size;
10775         }
10776         
10777         var settings=this;
10778         ['xs','sm','md','lg'].map(function(size){
10779             if (settings[size]) {
10780                 cfg.cls += ' col-' + size + '-' + settings[size];
10781             }
10782         });
10783         
10784         var inputblock = input;
10785         
10786         var feedback = {
10787             tag: 'span',
10788             cls: 'glyphicon form-control-feedback'
10789         };
10790             
10791         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10792             
10793             inputblock = {
10794                 cls : 'has-feedback',
10795                 cn :  [
10796                     input,
10797                     feedback
10798                 ] 
10799             };  
10800         }
10801         
10802         if (this.before || this.after) {
10803             
10804             inputblock = {
10805                 cls : 'input-group',
10806                 cn :  [] 
10807             };
10808             
10809             if (this.before && typeof(this.before) == 'string') {
10810                 
10811                 inputblock.cn.push({
10812                     tag :'span',
10813                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10814                     html : this.before
10815                 });
10816             }
10817             if (this.before && typeof(this.before) == 'object') {
10818                 this.before = Roo.factory(this.before);
10819                 
10820                 inputblock.cn.push({
10821                     tag :'span',
10822                     cls : 'roo-input-before input-group-prepend   input-group-' +
10823                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10824                 });
10825             }
10826             
10827             inputblock.cn.push(input);
10828             
10829             if (this.after && typeof(this.after) == 'string') {
10830                 inputblock.cn.push({
10831                     tag :'span',
10832                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10833                     html : this.after
10834                 });
10835             }
10836             if (this.after && typeof(this.after) == 'object') {
10837                 this.after = Roo.factory(this.after);
10838                 
10839                 inputblock.cn.push({
10840                     tag :'span',
10841                     cls : 'roo-input-after input-group-append  input-group-' +
10842                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10843                 });
10844             }
10845             
10846             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10847                 inputblock.cls += ' has-feedback';
10848                 inputblock.cn.push(feedback);
10849             }
10850         };
10851         var indicator = {
10852             tag : 'i',
10853             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10854             tooltip : 'This field is required'
10855         };
10856         if (this.allowBlank ) {
10857             indicator.style = this.allowBlank ? ' display:none' : '';
10858         }
10859         if (align ==='left' && this.fieldLabel.length) {
10860             
10861             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10862             
10863             cfg.cn = [
10864                 indicator,
10865                 {
10866                     tag: 'label',
10867                     'for' :  id,
10868                     cls : 'control-label col-form-label',
10869                     html : this.fieldLabel
10870
10871                 },
10872                 {
10873                     cls : "", 
10874                     cn: [
10875                         inputblock
10876                     ]
10877                 }
10878             ];
10879             
10880             var labelCfg = cfg.cn[1];
10881             var contentCfg = cfg.cn[2];
10882             
10883             if(this.indicatorpos == 'right'){
10884                 cfg.cn = [
10885                     {
10886                         tag: 'label',
10887                         'for' :  id,
10888                         cls : 'control-label col-form-label',
10889                         cn : [
10890                             {
10891                                 tag : 'span',
10892                                 html : this.fieldLabel
10893                             },
10894                             indicator
10895                         ]
10896                     },
10897                     {
10898                         cls : "",
10899                         cn: [
10900                             inputblock
10901                         ]
10902                     }
10903
10904                 ];
10905                 
10906                 labelCfg = cfg.cn[0];
10907                 contentCfg = cfg.cn[1];
10908             
10909             }
10910             
10911             if(this.labelWidth > 12){
10912                 labelCfg.style = "width: " + this.labelWidth + 'px';
10913             }
10914             
10915             if(this.labelWidth < 13 && this.labelmd == 0){
10916                 this.labelmd = this.labelWidth;
10917             }
10918             
10919             if(this.labellg > 0){
10920                 labelCfg.cls += ' col-lg-' + this.labellg;
10921                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10922             }
10923             
10924             if(this.labelmd > 0){
10925                 labelCfg.cls += ' col-md-' + this.labelmd;
10926                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10927             }
10928             
10929             if(this.labelsm > 0){
10930                 labelCfg.cls += ' col-sm-' + this.labelsm;
10931                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10932             }
10933             
10934             if(this.labelxs > 0){
10935                 labelCfg.cls += ' col-xs-' + this.labelxs;
10936                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10937             }
10938             
10939             
10940         } else if ( this.fieldLabel.length) {
10941                 
10942             
10943             
10944             cfg.cn = [
10945                 {
10946                     tag : 'i',
10947                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10948                     tooltip : 'This field is required',
10949                     style : this.allowBlank ? ' display:none' : '' 
10950                 },
10951                 {
10952                     tag: 'label',
10953                    //cls : 'input-group-addon',
10954                     html : this.fieldLabel
10955
10956                 },
10957
10958                inputblock
10959
10960            ];
10961            
10962            if(this.indicatorpos == 'right'){
10963        
10964                 cfg.cn = [
10965                     {
10966                         tag: 'label',
10967                        //cls : 'input-group-addon',
10968                         html : this.fieldLabel
10969
10970                     },
10971                     {
10972                         tag : 'i',
10973                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10974                         tooltip : 'This field is required',
10975                         style : this.allowBlank ? ' display:none' : '' 
10976                     },
10977
10978                    inputblock
10979
10980                ];
10981
10982             }
10983
10984         } else {
10985             
10986             cfg.cn = [
10987
10988                     inputblock
10989
10990             ];
10991                 
10992                 
10993         };
10994         
10995         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10996            cfg.cls += ' navbar-form';
10997         }
10998         
10999         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11000             // on BS4 we do this only if not form 
11001             cfg.cls += ' navbar-form';
11002             cfg.tag = 'li';
11003         }
11004         
11005         return cfg;
11006         
11007     },
11008     /**
11009      * return the real input element.
11010      */
11011     inputEl: function ()
11012     {
11013         return this.el.select('input.form-control',true).first();
11014     },
11015     
11016     tooltipEl : function()
11017     {
11018         return this.inputEl();
11019     },
11020     
11021     indicatorEl : function()
11022     {
11023         if (Roo.bootstrap.version == 4) {
11024             return false; // not enabled in v4 yet.
11025         }
11026         
11027         var indicator = this.el.select('i.roo-required-indicator',true).first();
11028         
11029         if(!indicator){
11030             return false;
11031         }
11032         
11033         return indicator;
11034         
11035     },
11036     
11037     setDisabled : function(v)
11038     {
11039         var i  = this.inputEl().dom;
11040         if (!v) {
11041             i.removeAttribute('disabled');
11042             return;
11043             
11044         }
11045         i.setAttribute('disabled','true');
11046     },
11047     initEvents : function()
11048     {
11049           
11050         this.inputEl().on("keydown" , this.fireKey,  this);
11051         this.inputEl().on("focus", this.onFocus,  this);
11052         this.inputEl().on("blur", this.onBlur,  this);
11053         
11054         this.inputEl().relayEvent('keyup', this);
11055         this.inputEl().relayEvent('paste', this);
11056         
11057         this.indicator = this.indicatorEl();
11058         
11059         if(this.indicator){
11060             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11061         }
11062  
11063         // reference to original value for reset
11064         this.originalValue = this.getValue();
11065         //Roo.form.TextField.superclass.initEvents.call(this);
11066         if(this.validationEvent == 'keyup'){
11067             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11068             this.inputEl().on('keyup', this.filterValidation, this);
11069         }
11070         else if(this.validationEvent !== false){
11071             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11072         }
11073         
11074         if(this.selectOnFocus){
11075             this.on("focus", this.preFocus, this);
11076             
11077         }
11078         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11079             this.inputEl().on("keypress", this.filterKeys, this);
11080         } else {
11081             this.inputEl().relayEvent('keypress', this);
11082         }
11083        /* if(this.grow){
11084             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11085             this.el.on("click", this.autoSize,  this);
11086         }
11087         */
11088         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11089             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11090         }
11091         
11092         if (typeof(this.before) == 'object') {
11093             this.before.render(this.el.select('.roo-input-before',true).first());
11094         }
11095         if (typeof(this.after) == 'object') {
11096             this.after.render(this.el.select('.roo-input-after',true).first());
11097         }
11098         
11099         this.inputEl().on('change', this.onChange, this);
11100         
11101     },
11102     filterValidation : function(e){
11103         if(!e.isNavKeyPress()){
11104             this.validationTask.delay(this.validationDelay);
11105         }
11106     },
11107      /**
11108      * Validates the field value
11109      * @return {Boolean} True if the value is valid, else false
11110      */
11111     validate : function(){
11112         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11113         if(this.disabled || this.validateValue(this.getRawValue())){
11114             this.markValid();
11115             return true;
11116         }
11117         
11118         this.markInvalid();
11119         return false;
11120     },
11121     
11122     
11123     /**
11124      * Validates a value according to the field's validation rules and marks the field as invalid
11125      * if the validation fails
11126      * @param {Mixed} value The value to validate
11127      * @return {Boolean} True if the value is valid, else false
11128      */
11129     validateValue : function(value)
11130     {
11131         if(this.getVisibilityEl().hasClass('hidden')){
11132             return true;
11133         }
11134         
11135         if(value.length < 1)  { // if it's blank
11136             if(this.allowBlank){
11137                 return true;
11138             }
11139             return false;
11140         }
11141         
11142         if(value.length < this.minLength){
11143             return false;
11144         }
11145         if(value.length > this.maxLength){
11146             return false;
11147         }
11148         if(this.vtype){
11149             var vt = Roo.form.VTypes;
11150             if(!vt[this.vtype](value, this)){
11151                 return false;
11152             }
11153         }
11154         if(typeof this.validator == "function"){
11155             var msg = this.validator(value);
11156             if(msg !== true){
11157                 return false;
11158             }
11159             if (typeof(msg) == 'string') {
11160                 this.invalidText = msg;
11161             }
11162         }
11163         
11164         if(this.regex && !this.regex.test(value)){
11165             return false;
11166         }
11167         
11168         return true;
11169     },
11170     
11171      // private
11172     fireKey : function(e){
11173         //Roo.log('field ' + e.getKey());
11174         if(e.isNavKeyPress()){
11175             this.fireEvent("specialkey", this, e);
11176         }
11177     },
11178     focus : function (selectText){
11179         if(this.rendered){
11180             this.inputEl().focus();
11181             if(selectText === true){
11182                 this.inputEl().dom.select();
11183             }
11184         }
11185         return this;
11186     } ,
11187     
11188     onFocus : function(){
11189         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11190            // this.el.addClass(this.focusClass);
11191         }
11192         if(!this.hasFocus){
11193             this.hasFocus = true;
11194             this.startValue = this.getValue();
11195             this.fireEvent("focus", this);
11196         }
11197     },
11198     
11199     beforeBlur : Roo.emptyFn,
11200
11201     
11202     // private
11203     onBlur : function(){
11204         this.beforeBlur();
11205         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11206             //this.el.removeClass(this.focusClass);
11207         }
11208         this.hasFocus = false;
11209         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11210             this.validate();
11211         }
11212         var v = this.getValue();
11213         if(String(v) !== String(this.startValue)){
11214             this.fireEvent('change', this, v, this.startValue);
11215         }
11216         this.fireEvent("blur", this);
11217     },
11218     
11219     onChange : function(e)
11220     {
11221         var v = this.getValue();
11222         if(String(v) !== String(this.startValue)){
11223             this.fireEvent('change', this, v, this.startValue);
11224         }
11225         
11226     },
11227     
11228     /**
11229      * Resets the current field value to the originally loaded value and clears any validation messages
11230      */
11231     reset : function(){
11232         this.setValue(this.originalValue);
11233         this.validate();
11234     },
11235      /**
11236      * Returns the name of the field
11237      * @return {Mixed} name The name field
11238      */
11239     getName: function(){
11240         return this.name;
11241     },
11242      /**
11243      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11244      * @return {Mixed} value The field value
11245      */
11246     getValue : function(){
11247         
11248         var v = this.inputEl().getValue();
11249         
11250         return v;
11251     },
11252     /**
11253      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11254      * @return {Mixed} value The field value
11255      */
11256     getRawValue : function(){
11257         var v = this.inputEl().getValue();
11258         
11259         return v;
11260     },
11261     
11262     /**
11263      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11264      * @param {Mixed} value The value to set
11265      */
11266     setRawValue : function(v){
11267         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11268     },
11269     
11270     selectText : function(start, end){
11271         var v = this.getRawValue();
11272         if(v.length > 0){
11273             start = start === undefined ? 0 : start;
11274             end = end === undefined ? v.length : end;
11275             var d = this.inputEl().dom;
11276             if(d.setSelectionRange){
11277                 d.setSelectionRange(start, end);
11278             }else if(d.createTextRange){
11279                 var range = d.createTextRange();
11280                 range.moveStart("character", start);
11281                 range.moveEnd("character", v.length-end);
11282                 range.select();
11283             }
11284         }
11285     },
11286     
11287     /**
11288      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11289      * @param {Mixed} value The value to set
11290      */
11291     setValue : function(v){
11292         this.value = v;
11293         if(this.rendered){
11294             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11295             this.validate();
11296         }
11297     },
11298     
11299     /*
11300     processValue : function(value){
11301         if(this.stripCharsRe){
11302             var newValue = value.replace(this.stripCharsRe, '');
11303             if(newValue !== value){
11304                 this.setRawValue(newValue);
11305                 return newValue;
11306             }
11307         }
11308         return value;
11309     },
11310   */
11311     preFocus : function(){
11312         
11313         if(this.selectOnFocus){
11314             this.inputEl().dom.select();
11315         }
11316     },
11317     filterKeys : function(e){
11318         var k = e.getKey();
11319         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11320             return;
11321         }
11322         var c = e.getCharCode(), cc = String.fromCharCode(c);
11323         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11324             return;
11325         }
11326         if(!this.maskRe.test(cc)){
11327             e.stopEvent();
11328         }
11329     },
11330      /**
11331      * Clear any invalid styles/messages for this field
11332      */
11333     clearInvalid : function(){
11334         
11335         if(!this.el || this.preventMark){ // not rendered
11336             return;
11337         }
11338         
11339         
11340         this.el.removeClass([this.invalidClass, 'is-invalid']);
11341         
11342         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11343             
11344             var feedback = this.el.select('.form-control-feedback', true).first();
11345             
11346             if(feedback){
11347                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11348             }
11349             
11350         }
11351         
11352         if(this.indicator){
11353             this.indicator.removeClass('visible');
11354             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11355         }
11356         
11357         this.fireEvent('valid', this);
11358     },
11359     
11360      /**
11361      * Mark this field as valid
11362      */
11363     markValid : function()
11364     {
11365         if(!this.el  || this.preventMark){ // not rendered...
11366             return;
11367         }
11368         
11369         this.el.removeClass([this.invalidClass, this.validClass]);
11370         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11371
11372         var feedback = this.el.select('.form-control-feedback', true).first();
11373             
11374         if(feedback){
11375             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11376         }
11377         
11378         if(this.indicator){
11379             this.indicator.removeClass('visible');
11380             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11381         }
11382         
11383         if(this.disabled){
11384             return;
11385         }
11386         
11387            
11388         if(this.allowBlank && !this.getRawValue().length){
11389             return;
11390         }
11391         if (Roo.bootstrap.version == 3) {
11392             this.el.addClass(this.validClass);
11393         } else {
11394             this.inputEl().addClass('is-valid');
11395         }
11396
11397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11398             
11399             var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401             if(feedback){
11402                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11403                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11404             }
11405             
11406         }
11407         
11408         this.fireEvent('valid', this);
11409     },
11410     
11411      /**
11412      * Mark this field as invalid
11413      * @param {String} msg The validation message
11414      */
11415     markInvalid : function(msg)
11416     {
11417         if(!this.el  || this.preventMark){ // not rendered
11418             return;
11419         }
11420         
11421         this.el.removeClass([this.invalidClass, this.validClass]);
11422         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11423         
11424         var feedback = this.el.select('.form-control-feedback', true).first();
11425             
11426         if(feedback){
11427             this.el.select('.form-control-feedback', true).first().removeClass(
11428                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11429         }
11430
11431         if(this.disabled){
11432             return;
11433         }
11434         
11435         if(this.allowBlank && !this.getRawValue().length){
11436             return;
11437         }
11438         
11439         if(this.indicator){
11440             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11441             this.indicator.addClass('visible');
11442         }
11443         if (Roo.bootstrap.version == 3) {
11444             this.el.addClass(this.invalidClass);
11445         } else {
11446             this.inputEl().addClass('is-invalid');
11447         }
11448         
11449         
11450         
11451         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11452             
11453             var feedback = this.el.select('.form-control-feedback', true).first();
11454             
11455             if(feedback){
11456                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11457                 
11458                 if(this.getValue().length || this.forceFeedback){
11459                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11460                 }
11461                 
11462             }
11463             
11464         }
11465         
11466         this.fireEvent('invalid', this, msg);
11467     },
11468     // private
11469     SafariOnKeyDown : function(event)
11470     {
11471         // this is a workaround for a password hang bug on chrome/ webkit.
11472         if (this.inputEl().dom.type != 'password') {
11473             return;
11474         }
11475         
11476         var isSelectAll = false;
11477         
11478         if(this.inputEl().dom.selectionEnd > 0){
11479             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11480         }
11481         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11482             event.preventDefault();
11483             this.setValue('');
11484             return;
11485         }
11486         
11487         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11488             
11489             event.preventDefault();
11490             // this is very hacky as keydown always get's upper case.
11491             //
11492             var cc = String.fromCharCode(event.getCharCode());
11493             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11494             
11495         }
11496     },
11497     adjustWidth : function(tag, w){
11498         tag = tag.toLowerCase();
11499         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11500             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11501                 if(tag == 'input'){
11502                     return w + 2;
11503                 }
11504                 if(tag == 'textarea'){
11505                     return w-2;
11506                 }
11507             }else if(Roo.isOpera){
11508                 if(tag == 'input'){
11509                     return w + 2;
11510                 }
11511                 if(tag == 'textarea'){
11512                     return w-2;
11513                 }
11514             }
11515         }
11516         return w;
11517     },
11518     
11519     setFieldLabel : function(v)
11520     {
11521         if(!this.rendered){
11522             return;
11523         }
11524         
11525         if(this.indicatorEl()){
11526             var ar = this.el.select('label > span',true);
11527             
11528             if (ar.elements.length) {
11529                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11530                 this.fieldLabel = v;
11531                 return;
11532             }
11533             
11534             var br = this.el.select('label',true);
11535             
11536             if(br.elements.length) {
11537                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11538                 this.fieldLabel = v;
11539                 return;
11540             }
11541             
11542             Roo.log('Cannot Found any of label > span || label in input');
11543             return;
11544         }
11545         
11546         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11547         this.fieldLabel = v;
11548         
11549         
11550     }
11551 });
11552
11553  
11554 /*
11555  * - LGPL
11556  *
11557  * Input
11558  * 
11559  */
11560
11561 /**
11562  * @class Roo.bootstrap.TextArea
11563  * @extends Roo.bootstrap.Input
11564  * Bootstrap TextArea class
11565  * @cfg {Number} cols Specifies the visible width of a text area
11566  * @cfg {Number} rows Specifies the visible number of lines in a text area
11567  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11568  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11569  * @cfg {string} html text
11570  * 
11571  * @constructor
11572  * Create a new TextArea
11573  * @param {Object} config The config object
11574  */
11575
11576 Roo.bootstrap.TextArea = function(config){
11577     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11578    
11579 };
11580
11581 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11582      
11583     cols : false,
11584     rows : 5,
11585     readOnly : false,
11586     warp : 'soft',
11587     resize : false,
11588     value: false,
11589     html: false,
11590     
11591     getAutoCreate : function(){
11592         
11593         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11594         
11595         var id = Roo.id();
11596         
11597         var cfg = {};
11598         
11599         if(this.inputType != 'hidden'){
11600             cfg.cls = 'form-group' //input-group
11601         }
11602         
11603         var input =  {
11604             tag: 'textarea',
11605             id : id,
11606             warp : this.warp,
11607             rows : this.rows,
11608             value : this.value || '',
11609             html: this.html || '',
11610             cls : 'form-control',
11611             placeholder : this.placeholder || '' 
11612             
11613         };
11614         
11615         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11616             input.maxLength = this.maxLength;
11617         }
11618         
11619         if(this.resize){
11620             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11621         }
11622         
11623         if(this.cols){
11624             input.cols = this.cols;
11625         }
11626         
11627         if (this.readOnly) {
11628             input.readonly = true;
11629         }
11630         
11631         if (this.name) {
11632             input.name = this.name;
11633         }
11634         
11635         if (this.size) {
11636             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11637         }
11638         
11639         var settings=this;
11640         ['xs','sm','md','lg'].map(function(size){
11641             if (settings[size]) {
11642                 cfg.cls += ' col-' + size + '-' + settings[size];
11643             }
11644         });
11645         
11646         var inputblock = input;
11647         
11648         if(this.hasFeedback && !this.allowBlank){
11649             
11650             var feedback = {
11651                 tag: 'span',
11652                 cls: 'glyphicon form-control-feedback'
11653             };
11654
11655             inputblock = {
11656                 cls : 'has-feedback',
11657                 cn :  [
11658                     input,
11659                     feedback
11660                 ] 
11661             };  
11662         }
11663         
11664         
11665         if (this.before || this.after) {
11666             
11667             inputblock = {
11668                 cls : 'input-group',
11669                 cn :  [] 
11670             };
11671             if (this.before) {
11672                 inputblock.cn.push({
11673                     tag :'span',
11674                     cls : 'input-group-addon',
11675                     html : this.before
11676                 });
11677             }
11678             
11679             inputblock.cn.push(input);
11680             
11681             if(this.hasFeedback && !this.allowBlank){
11682                 inputblock.cls += ' has-feedback';
11683                 inputblock.cn.push(feedback);
11684             }
11685             
11686             if (this.after) {
11687                 inputblock.cn.push({
11688                     tag :'span',
11689                     cls : 'input-group-addon',
11690                     html : this.after
11691                 });
11692             }
11693             
11694         }
11695         
11696         if (align ==='left' && this.fieldLabel.length) {
11697             cfg.cn = [
11698                 {
11699                     tag: 'label',
11700                     'for' :  id,
11701                     cls : 'control-label',
11702                     html : this.fieldLabel
11703                 },
11704                 {
11705                     cls : "",
11706                     cn: [
11707                         inputblock
11708                     ]
11709                 }
11710
11711             ];
11712             
11713             if(this.labelWidth > 12){
11714                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11715             }
11716
11717             if(this.labelWidth < 13 && this.labelmd == 0){
11718                 this.labelmd = this.labelWidth;
11719             }
11720
11721             if(this.labellg > 0){
11722                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11723                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11724             }
11725
11726             if(this.labelmd > 0){
11727                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11728                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11729             }
11730
11731             if(this.labelsm > 0){
11732                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11733                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11734             }
11735
11736             if(this.labelxs > 0){
11737                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11738                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11739             }
11740             
11741         } else if ( this.fieldLabel.length) {
11742             cfg.cn = [
11743
11744                {
11745                    tag: 'label',
11746                    //cls : 'input-group-addon',
11747                    html : this.fieldLabel
11748
11749                },
11750
11751                inputblock
11752
11753            ];
11754
11755         } else {
11756
11757             cfg.cn = [
11758
11759                 inputblock
11760
11761             ];
11762                 
11763         }
11764         
11765         if (this.disabled) {
11766             input.disabled=true;
11767         }
11768         
11769         return cfg;
11770         
11771     },
11772     /**
11773      * return the real textarea element.
11774      */
11775     inputEl: function ()
11776     {
11777         return this.el.select('textarea.form-control',true).first();
11778     },
11779     
11780     /**
11781      * Clear any invalid styles/messages for this field
11782      */
11783     clearInvalid : function()
11784     {
11785         
11786         if(!this.el || this.preventMark){ // not rendered
11787             return;
11788         }
11789         
11790         var label = this.el.select('label', true).first();
11791         var icon = this.el.select('i.fa-star', true).first();
11792         
11793         if(label && icon){
11794             icon.remove();
11795         }
11796         this.el.removeClass( this.validClass);
11797         this.inputEl().removeClass('is-invalid');
11798          
11799         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11800             
11801             var feedback = this.el.select('.form-control-feedback', true).first();
11802             
11803             if(feedback){
11804                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11805             }
11806             
11807         }
11808         
11809         this.fireEvent('valid', this);
11810     },
11811     
11812      /**
11813      * Mark this field as valid
11814      */
11815     markValid : function()
11816     {
11817         if(!this.el  || this.preventMark){ // not rendered
11818             return;
11819         }
11820         
11821         this.el.removeClass([this.invalidClass, this.validClass]);
11822         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11823         
11824         var feedback = this.el.select('.form-control-feedback', true).first();
11825             
11826         if(feedback){
11827             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11828         }
11829
11830         if(this.disabled || this.allowBlank){
11831             return;
11832         }
11833         
11834         var label = this.el.select('label', true).first();
11835         var icon = this.el.select('i.fa-star', true).first();
11836         
11837         if(label && icon){
11838             icon.remove();
11839         }
11840         if (Roo.bootstrap.version == 3) {
11841             this.el.addClass(this.validClass);
11842         } else {
11843             this.inputEl().addClass('is-valid');
11844         }
11845         
11846         
11847         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11848             
11849             var feedback = this.el.select('.form-control-feedback', true).first();
11850             
11851             if(feedback){
11852                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11853                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11854             }
11855             
11856         }
11857         
11858         this.fireEvent('valid', this);
11859     },
11860     
11861      /**
11862      * Mark this field as invalid
11863      * @param {String} msg The validation message
11864      */
11865     markInvalid : function(msg)
11866     {
11867         if(!this.el  || this.preventMark){ // not rendered
11868             return;
11869         }
11870         
11871         this.el.removeClass([this.invalidClass, this.validClass]);
11872         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11873         
11874         var feedback = this.el.select('.form-control-feedback', true).first();
11875             
11876         if(feedback){
11877             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11878         }
11879
11880         if(this.disabled || this.allowBlank){
11881             return;
11882         }
11883         
11884         var label = this.el.select('label', true).first();
11885         var icon = this.el.select('i.fa-star', true).first();
11886         
11887         if(!this.getValue().length && label && !icon){
11888             this.el.createChild({
11889                 tag : 'i',
11890                 cls : 'text-danger fa fa-lg fa-star',
11891                 tooltip : 'This field is required',
11892                 style : 'margin-right:5px;'
11893             }, label, true);
11894         }
11895         
11896         if (Roo.bootstrap.version == 3) {
11897             this.el.addClass(this.invalidClass);
11898         } else {
11899             this.inputEl().addClass('is-invalid');
11900         }
11901         
11902         // fixme ... this may be depricated need to test..
11903         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11904             
11905             var feedback = this.el.select('.form-control-feedback', true).first();
11906             
11907             if(feedback){
11908                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11909                 
11910                 if(this.getValue().length || this.forceFeedback){
11911                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11912                 }
11913                 
11914             }
11915             
11916         }
11917         
11918         this.fireEvent('invalid', this, msg);
11919     }
11920 });
11921
11922  
11923 /*
11924  * - LGPL
11925  *
11926  * trigger field - base class for combo..
11927  * 
11928  */
11929  
11930 /**
11931  * @class Roo.bootstrap.TriggerField
11932  * @extends Roo.bootstrap.Input
11933  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11934  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11935  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11936  * for which you can provide a custom implementation.  For example:
11937  * <pre><code>
11938 var trigger = new Roo.bootstrap.TriggerField();
11939 trigger.onTriggerClick = myTriggerFn;
11940 trigger.applyTo('my-field');
11941 </code></pre>
11942  *
11943  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11944  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11945  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11946  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11947  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11948
11949  * @constructor
11950  * Create a new TriggerField.
11951  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11952  * to the base TextField)
11953  */
11954 Roo.bootstrap.TriggerField = function(config){
11955     this.mimicing = false;
11956     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11957 };
11958
11959 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11960     /**
11961      * @cfg {String} triggerClass A CSS class to apply to the trigger
11962      */
11963      /**
11964      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11965      */
11966     hideTrigger:false,
11967
11968     /**
11969      * @cfg {Boolean} removable (true|false) special filter default false
11970      */
11971     removable : false,
11972     
11973     /** @cfg {Boolean} grow @hide */
11974     /** @cfg {Number} growMin @hide */
11975     /** @cfg {Number} growMax @hide */
11976
11977     /**
11978      * @hide 
11979      * @method
11980      */
11981     autoSize: Roo.emptyFn,
11982     // private
11983     monitorTab : true,
11984     // private
11985     deferHeight : true,
11986
11987     
11988     actionMode : 'wrap',
11989     
11990     caret : false,
11991     
11992     
11993     getAutoCreate : function(){
11994        
11995         var align = this.labelAlign || this.parentLabelAlign();
11996         
11997         var id = Roo.id();
11998         
11999         var cfg = {
12000             cls: 'form-group' //input-group
12001         };
12002         
12003         
12004         var input =  {
12005             tag: 'input',
12006             id : id,
12007             type : this.inputType,
12008             cls : 'form-control',
12009             autocomplete: 'new-password',
12010             placeholder : this.placeholder || '' 
12011             
12012         };
12013         if (this.name) {
12014             input.name = this.name;
12015         }
12016         if (this.size) {
12017             input.cls += ' input-' + this.size;
12018         }
12019         
12020         if (this.disabled) {
12021             input.disabled=true;
12022         }
12023         
12024         var inputblock = input;
12025         
12026         if(this.hasFeedback && !this.allowBlank){
12027             
12028             var feedback = {
12029                 tag: 'span',
12030                 cls: 'glyphicon form-control-feedback'
12031             };
12032             
12033             if(this.removable && !this.editable  ){
12034                 inputblock = {
12035                     cls : 'has-feedback',
12036                     cn :  [
12037                         inputblock,
12038                         {
12039                             tag: 'button',
12040                             html : 'x',
12041                             cls : 'roo-combo-removable-btn close'
12042                         },
12043                         feedback
12044                     ] 
12045                 };
12046             } else {
12047                 inputblock = {
12048                     cls : 'has-feedback',
12049                     cn :  [
12050                         inputblock,
12051                         feedback
12052                     ] 
12053                 };
12054             }
12055
12056         } else {
12057             if(this.removable && !this.editable ){
12058                 inputblock = {
12059                     cls : 'roo-removable',
12060                     cn :  [
12061                         inputblock,
12062                         {
12063                             tag: 'button',
12064                             html : 'x',
12065                             cls : 'roo-combo-removable-btn close'
12066                         }
12067                     ] 
12068                 };
12069             }
12070         }
12071         
12072         if (this.before || this.after) {
12073             
12074             inputblock = {
12075                 cls : 'input-group',
12076                 cn :  [] 
12077             };
12078             if (this.before) {
12079                 inputblock.cn.push({
12080                     tag :'span',
12081                     cls : 'input-group-addon input-group-prepend input-group-text',
12082                     html : this.before
12083                 });
12084             }
12085             
12086             inputblock.cn.push(input);
12087             
12088             if(this.hasFeedback && !this.allowBlank){
12089                 inputblock.cls += ' has-feedback';
12090                 inputblock.cn.push(feedback);
12091             }
12092             
12093             if (this.after) {
12094                 inputblock.cn.push({
12095                     tag :'span',
12096                     cls : 'input-group-addon input-group-append input-group-text',
12097                     html : this.after
12098                 });
12099             }
12100             
12101         };
12102         
12103       
12104         
12105         var ibwrap = inputblock;
12106         
12107         if(this.multiple){
12108             ibwrap = {
12109                 tag: 'ul',
12110                 cls: 'roo-select2-choices',
12111                 cn:[
12112                     {
12113                         tag: 'li',
12114                         cls: 'roo-select2-search-field',
12115                         cn: [
12116
12117                             inputblock
12118                         ]
12119                     }
12120                 ]
12121             };
12122                 
12123         }
12124         
12125         var combobox = {
12126             cls: 'roo-select2-container input-group',
12127             cn: [
12128                  {
12129                     tag: 'input',
12130                     type : 'hidden',
12131                     cls: 'form-hidden-field'
12132                 },
12133                 ibwrap
12134             ]
12135         };
12136         
12137         if(!this.multiple && this.showToggleBtn){
12138             
12139             var caret = {
12140                         tag: 'span',
12141                         cls: 'caret'
12142              };
12143             if (this.caret != false) {
12144                 caret = {
12145                      tag: 'i',
12146                      cls: 'fa fa-' + this.caret
12147                 };
12148                 
12149             }
12150             
12151             combobox.cn.push({
12152                 tag :'span',
12153                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12154                 cn : [
12155                     Roo.bootstrap.version == 3 ? caret : '',
12156                     {
12157                         tag: 'span',
12158                         cls: 'combobox-clear',
12159                         cn  : [
12160                             {
12161                                 tag : 'i',
12162                                 cls: 'icon-remove'
12163                             }
12164                         ]
12165                     }
12166                 ]
12167
12168             })
12169         }
12170         
12171         if(this.multiple){
12172             combobox.cls += ' roo-select2-container-multi';
12173         }
12174          var indicator = {
12175             tag : 'i',
12176             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12177             tooltip : 'This field is required'
12178         };
12179         if (Roo.bootstrap.version == 4) {
12180             indicator = {
12181                 tag : 'i',
12182                 style : 'display:none'
12183             };
12184         }
12185         
12186         
12187         if (align ==='left' && this.fieldLabel.length) {
12188             
12189             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12190
12191             cfg.cn = [
12192                 indicator,
12193                 {
12194                     tag: 'label',
12195                     'for' :  id,
12196                     cls : 'control-label',
12197                     html : this.fieldLabel
12198
12199                 },
12200                 {
12201                     cls : "", 
12202                     cn: [
12203                         combobox
12204                     ]
12205                 }
12206
12207             ];
12208             
12209             var labelCfg = cfg.cn[1];
12210             var contentCfg = cfg.cn[2];
12211             
12212             if(this.indicatorpos == 'right'){
12213                 cfg.cn = [
12214                     {
12215                         tag: 'label',
12216                         'for' :  id,
12217                         cls : 'control-label',
12218                         cn : [
12219                             {
12220                                 tag : 'span',
12221                                 html : this.fieldLabel
12222                             },
12223                             indicator
12224                         ]
12225                     },
12226                     {
12227                         cls : "", 
12228                         cn: [
12229                             combobox
12230                         ]
12231                     }
12232
12233                 ];
12234                 
12235                 labelCfg = cfg.cn[0];
12236                 contentCfg = cfg.cn[1];
12237             }
12238             
12239             if(this.labelWidth > 12){
12240                 labelCfg.style = "width: " + this.labelWidth + 'px';
12241             }
12242             
12243             if(this.labelWidth < 13 && this.labelmd == 0){
12244                 this.labelmd = this.labelWidth;
12245             }
12246             
12247             if(this.labellg > 0){
12248                 labelCfg.cls += ' col-lg-' + this.labellg;
12249                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12250             }
12251             
12252             if(this.labelmd > 0){
12253                 labelCfg.cls += ' col-md-' + this.labelmd;
12254                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12255             }
12256             
12257             if(this.labelsm > 0){
12258                 labelCfg.cls += ' col-sm-' + this.labelsm;
12259                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12260             }
12261             
12262             if(this.labelxs > 0){
12263                 labelCfg.cls += ' col-xs-' + this.labelxs;
12264                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12265             }
12266             
12267         } else if ( this.fieldLabel.length) {
12268 //                Roo.log(" label");
12269             cfg.cn = [
12270                 indicator,
12271                {
12272                    tag: 'label',
12273                    //cls : 'input-group-addon',
12274                    html : this.fieldLabel
12275
12276                },
12277
12278                combobox
12279
12280             ];
12281             
12282             if(this.indicatorpos == 'right'){
12283                 
12284                 cfg.cn = [
12285                     {
12286                        tag: 'label',
12287                        cn : [
12288                            {
12289                                tag : 'span',
12290                                html : this.fieldLabel
12291                            },
12292                            indicator
12293                        ]
12294
12295                     },
12296                     combobox
12297
12298                 ];
12299
12300             }
12301
12302         } else {
12303             
12304 //                Roo.log(" no label && no align");
12305                 cfg = combobox
12306                      
12307                 
12308         }
12309         
12310         var settings=this;
12311         ['xs','sm','md','lg'].map(function(size){
12312             if (settings[size]) {
12313                 cfg.cls += ' col-' + size + '-' + settings[size];
12314             }
12315         });
12316         
12317         return cfg;
12318         
12319     },
12320     
12321     
12322     
12323     // private
12324     onResize : function(w, h){
12325 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12326 //        if(typeof w == 'number'){
12327 //            var x = w - this.trigger.getWidth();
12328 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12329 //            this.trigger.setStyle('left', x+'px');
12330 //        }
12331     },
12332
12333     // private
12334     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12335
12336     // private
12337     getResizeEl : function(){
12338         return this.inputEl();
12339     },
12340
12341     // private
12342     getPositionEl : function(){
12343         return this.inputEl();
12344     },
12345
12346     // private
12347     alignErrorIcon : function(){
12348         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12349     },
12350
12351     // private
12352     initEvents : function(){
12353         
12354         this.createList();
12355         
12356         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12357         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12358         if(!this.multiple && this.showToggleBtn){
12359             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12360             if(this.hideTrigger){
12361                 this.trigger.setDisplayed(false);
12362             }
12363             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12364         }
12365         
12366         if(this.multiple){
12367             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12368         }
12369         
12370         if(this.removable && !this.editable && !this.tickable){
12371             var close = this.closeTriggerEl();
12372             
12373             if(close){
12374                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12375                 close.on('click', this.removeBtnClick, this, close);
12376             }
12377         }
12378         
12379         //this.trigger.addClassOnOver('x-form-trigger-over');
12380         //this.trigger.addClassOnClick('x-form-trigger-click');
12381         
12382         //if(!this.width){
12383         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12384         //}
12385     },
12386     
12387     closeTriggerEl : function()
12388     {
12389         var close = this.el.select('.roo-combo-removable-btn', true).first();
12390         return close ? close : false;
12391     },
12392     
12393     removeBtnClick : function(e, h, el)
12394     {
12395         e.preventDefault();
12396         
12397         if(this.fireEvent("remove", this) !== false){
12398             this.reset();
12399             this.fireEvent("afterremove", this)
12400         }
12401     },
12402     
12403     createList : function()
12404     {
12405         this.list = Roo.get(document.body).createChild({
12406             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12407             cls: 'typeahead typeahead-long dropdown-menu shadow',
12408             style: 'display:none'
12409         });
12410         
12411         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12412         
12413     },
12414
12415     // private
12416     initTrigger : function(){
12417        
12418     },
12419
12420     // private
12421     onDestroy : function(){
12422         if(this.trigger){
12423             this.trigger.removeAllListeners();
12424           //  this.trigger.remove();
12425         }
12426         //if(this.wrap){
12427         //    this.wrap.remove();
12428         //}
12429         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12430     },
12431
12432     // private
12433     onFocus : function(){
12434         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12435         /*
12436         if(!this.mimicing){
12437             this.wrap.addClass('x-trigger-wrap-focus');
12438             this.mimicing = true;
12439             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12440             if(this.monitorTab){
12441                 this.el.on("keydown", this.checkTab, this);
12442             }
12443         }
12444         */
12445     },
12446
12447     // private
12448     checkTab : function(e){
12449         if(e.getKey() == e.TAB){
12450             this.triggerBlur();
12451         }
12452     },
12453
12454     // private
12455     onBlur : function(){
12456         // do nothing
12457     },
12458
12459     // private
12460     mimicBlur : function(e, t){
12461         /*
12462         if(!this.wrap.contains(t) && this.validateBlur()){
12463             this.triggerBlur();
12464         }
12465         */
12466     },
12467
12468     // private
12469     triggerBlur : function(){
12470         this.mimicing = false;
12471         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12472         if(this.monitorTab){
12473             this.el.un("keydown", this.checkTab, this);
12474         }
12475         //this.wrap.removeClass('x-trigger-wrap-focus');
12476         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12477     },
12478
12479     // private
12480     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12481     validateBlur : function(e, t){
12482         return true;
12483     },
12484
12485     // private
12486     onDisable : function(){
12487         this.inputEl().dom.disabled = true;
12488         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12489         //if(this.wrap){
12490         //    this.wrap.addClass('x-item-disabled');
12491         //}
12492     },
12493
12494     // private
12495     onEnable : function(){
12496         this.inputEl().dom.disabled = false;
12497         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12498         //if(this.wrap){
12499         //    this.el.removeClass('x-item-disabled');
12500         //}
12501     },
12502
12503     // private
12504     onShow : function(){
12505         var ae = this.getActionEl();
12506         
12507         if(ae){
12508             ae.dom.style.display = '';
12509             ae.dom.style.visibility = 'visible';
12510         }
12511     },
12512
12513     // private
12514     
12515     onHide : function(){
12516         var ae = this.getActionEl();
12517         ae.dom.style.display = 'none';
12518     },
12519
12520     /**
12521      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12522      * by an implementing function.
12523      * @method
12524      * @param {EventObject} e
12525      */
12526     onTriggerClick : Roo.emptyFn
12527 });
12528  
12529 /*
12530 * Licence: LGPL
12531 */
12532
12533 /**
12534  * @class Roo.bootstrap.CardUploader
12535  * @extends Roo.bootstrap.Button
12536  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12537  * @cfg {Number} errorTimeout default 3000
12538  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12539  * @cfg {Array}  html The button text.
12540
12541  *
12542  * @constructor
12543  * Create a new CardUploader
12544  * @param {Object} config The config object
12545  */
12546
12547 Roo.bootstrap.CardUploader = function(config){
12548     
12549  
12550     
12551     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12552     
12553     
12554     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12555         return r.data.id
12556      });
12557     
12558      this.addEvents({
12559          // raw events
12560         /**
12561          * @event preview
12562          * When a image is clicked on - and needs to display a slideshow or similar..
12563          * @param {Roo.bootstrap.Card} this
12564          * @param {Object} The image information data 
12565          *
12566          */
12567         'preview' : true,
12568          /**
12569          * @event download
12570          * When a the download link is clicked
12571          * @param {Roo.bootstrap.Card} this
12572          * @param {Object} The image information data  contains 
12573          */
12574         'download' : true
12575         
12576     });
12577 };
12578  
12579 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12580     
12581      
12582     errorTimeout : 3000,
12583      
12584     images : false,
12585    
12586     fileCollection : false,
12587     allowBlank : true,
12588     
12589     getAutoCreate : function()
12590     {
12591         
12592         var cfg =  {
12593             cls :'form-group' ,
12594             cn : [
12595                
12596                 {
12597                     tag: 'label',
12598                    //cls : 'input-group-addon',
12599                     html : this.fieldLabel
12600
12601                 },
12602
12603                 {
12604                     tag: 'input',
12605                     type : 'hidden',
12606                     name : this.name,
12607                     value : this.value,
12608                     cls : 'd-none  form-control'
12609                 },
12610                 
12611                 {
12612                     tag: 'input',
12613                     multiple : 'multiple',
12614                     type : 'file',
12615                     cls : 'd-none  roo-card-upload-selector'
12616                 },
12617                 
12618                 {
12619                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12620                 },
12621                 {
12622                     cls : 'card-columns roo-card-uploader-container'
12623                 }
12624
12625             ]
12626         };
12627            
12628          
12629         return cfg;
12630     },
12631     
12632     getChildContainer : function() /// what children are added to.
12633     {
12634         return this.containerEl;
12635     },
12636    
12637     getButtonContainer : function() /// what children are added to.
12638     {
12639         return this.el.select(".roo-card-uploader-button-container").first();
12640     },
12641    
12642     initEvents : function()
12643     {
12644         
12645         Roo.bootstrap.Input.prototype.initEvents.call(this);
12646         
12647         var t = this;
12648         this.addxtype({
12649             xns: Roo.bootstrap,
12650
12651             xtype : 'Button',
12652             container_method : 'getButtonContainer' ,            
12653             html :  this.html, // fix changable?
12654             cls : 'w-100 ',
12655             listeners : {
12656                 'click' : function(btn, e) {
12657                     t.onClick(e);
12658                 }
12659             }
12660         });
12661         
12662         
12663         
12664         
12665         this.urlAPI = (window.createObjectURL && window) || 
12666                                 (window.URL && URL.revokeObjectURL && URL) || 
12667                                 (window.webkitURL && webkitURL);
12668                         
12669          
12670          
12671          
12672         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12673         
12674         this.selectorEl.on('change', this.onFileSelected, this);
12675         if (this.images) {
12676             var t = this;
12677             this.images.forEach(function(img) {
12678                 t.addCard(img)
12679             });
12680             this.images = false;
12681         }
12682         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12683          
12684        
12685     },
12686     
12687    
12688     onClick : function(e)
12689     {
12690         e.preventDefault();
12691          
12692         this.selectorEl.dom.click();
12693          
12694     },
12695     
12696     onFileSelected : function(e)
12697     {
12698         e.preventDefault();
12699         
12700         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12701             return;
12702         }
12703         
12704         Roo.each(this.selectorEl.dom.files, function(file){    
12705             this.addFile(file);
12706         }, this);
12707          
12708     },
12709     
12710       
12711     
12712       
12713     
12714     addFile : function(file)
12715     {
12716            
12717         if(typeof(file) === 'string'){
12718             throw "Add file by name?"; // should not happen
12719             return;
12720         }
12721         
12722         if(!file || !this.urlAPI){
12723             return;
12724         }
12725         
12726         // file;
12727         // file.type;
12728         
12729         var _this = this;
12730         
12731         
12732         var url = _this.urlAPI.createObjectURL( file);
12733            
12734         this.addCard({
12735             id : Roo.bootstrap.CardUploader.ID--,
12736             is_uploaded : false,
12737             src : url,
12738             srcfile : file,
12739             title : file.name,
12740             mimetype : file.type,
12741             preview : false,
12742             is_deleted : 0
12743         });
12744         
12745     },
12746     
12747     /**
12748      * addCard - add an Attachment to the uploader
12749      * @param data - the data about the image to upload
12750      *
12751      * {
12752           id : 123
12753           title : "Title of file",
12754           is_uploaded : false,
12755           src : "http://.....",
12756           srcfile : { the File upload object },
12757           mimetype : file.type,
12758           preview : false,
12759           is_deleted : 0
12760           .. any other data...
12761         }
12762      *
12763      * 
12764     */
12765     
12766     addCard : function (data)
12767     {
12768         // hidden input element?
12769         // if the file is not an image...
12770         //then we need to use something other that and header_image
12771         var t = this;
12772         //   remove.....
12773         var footer = [
12774             {
12775                 xns : Roo.bootstrap,
12776                 xtype : 'CardFooter',
12777                  items: [
12778                     {
12779                         xns : Roo.bootstrap,
12780                         xtype : 'Element',
12781                         cls : 'd-flex',
12782                         items : [
12783                             
12784                             {
12785                                 xns : Roo.bootstrap,
12786                                 xtype : 'Button',
12787                                 html : String.format("<small>{0}</small>", data.title),
12788                                 cls : 'col-10 text-left',
12789                                 size: 'sm',
12790                                 weight: 'link',
12791                                 fa : 'download',
12792                                 listeners : {
12793                                     click : function() {
12794                                      
12795                                         t.fireEvent( "download", t, data );
12796                                     }
12797                                 }
12798                             },
12799                           
12800                             {
12801                                 xns : Roo.bootstrap,
12802                                 xtype : 'Button',
12803                                 style: 'max-height: 28px; ',
12804                                 size : 'sm',
12805                                 weight: 'danger',
12806                                 cls : 'col-2',
12807                                 fa : 'times',
12808                                 listeners : {
12809                                     click : function() {
12810                                         t.removeCard(data.id)
12811                                     }
12812                                 }
12813                             }
12814                         ]
12815                     }
12816                     
12817                 ] 
12818             }
12819             
12820         ];
12821         
12822         var cn = this.addxtype(
12823             {
12824                  
12825                 xns : Roo.bootstrap,
12826                 xtype : 'Card',
12827                 closeable : true,
12828                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12829                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12830                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12831                 data : data,
12832                 html : false,
12833                  
12834                 items : footer,
12835                 initEvents : function() {
12836                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12837                     var card = this;
12838                     this.imgEl = this.el.select('.card-img-top').first();
12839                     if (this.imgEl) {
12840                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12841                         this.imgEl.set({ 'pointer' : 'cursor' });
12842                                   
12843                     }
12844                     this.getCardFooter().addClass('p-1');
12845                     
12846                   
12847                 }
12848                 
12849             }
12850         );
12851         // dont' really need ot update items.
12852         // this.items.push(cn);
12853         this.fileCollection.add(cn);
12854         
12855         if (!data.srcfile) {
12856             this.updateInput();
12857             return;
12858         }
12859             
12860         var _t = this;
12861         var reader = new FileReader();
12862         reader.addEventListener("load", function() {  
12863             data.srcdata =  reader.result;
12864             _t.updateInput();
12865         });
12866         reader.readAsDataURL(data.srcfile);
12867         
12868         
12869         
12870     },
12871     removeCard : function(id)
12872     {
12873         
12874         var card  = this.fileCollection.get(id);
12875         card.data.is_deleted = 1;
12876         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12877         //this.fileCollection.remove(card);
12878         //this.items = this.items.filter(function(e) { return e != card });
12879         // dont' really need ot update items.
12880         card.el.dom.parentNode.removeChild(card.el.dom);
12881         this.updateInput();
12882
12883         
12884     },
12885     reset: function()
12886     {
12887         this.fileCollection.each(function(card) {
12888             if (card.el.dom && card.el.dom.parentNode) {
12889                 card.el.dom.parentNode.removeChild(card.el.dom);
12890             }
12891         });
12892         this.fileCollection.clear();
12893         this.updateInput();
12894     },
12895     
12896     updateInput : function()
12897     {
12898          var data = [];
12899         this.fileCollection.each(function(e) {
12900             data.push(e.data);
12901             
12902         });
12903         this.inputEl().dom.value = JSON.stringify(data);
12904         
12905         
12906         
12907     }
12908     
12909     
12910 });
12911
12912
12913 Roo.bootstrap.CardUploader.ID = -1;/*
12914  * Based on:
12915  * Ext JS Library 1.1.1
12916  * Copyright(c) 2006-2007, Ext JS, LLC.
12917  *
12918  * Originally Released Under LGPL - original licence link has changed is not relivant.
12919  *
12920  * Fork - LGPL
12921  * <script type="text/javascript">
12922  */
12923
12924
12925 /**
12926  * @class Roo.data.SortTypes
12927  * @singleton
12928  * Defines the default sorting (casting?) comparison functions used when sorting data.
12929  */
12930 Roo.data.SortTypes = {
12931     /**
12932      * Default sort that does nothing
12933      * @param {Mixed} s The value being converted
12934      * @return {Mixed} The comparison value
12935      */
12936     none : function(s){
12937         return s;
12938     },
12939     
12940     /**
12941      * The regular expression used to strip tags
12942      * @type {RegExp}
12943      * @property
12944      */
12945     stripTagsRE : /<\/?[^>]+>/gi,
12946     
12947     /**
12948      * Strips all HTML tags to sort on text only
12949      * @param {Mixed} s The value being converted
12950      * @return {String} The comparison value
12951      */
12952     asText : function(s){
12953         return String(s).replace(this.stripTagsRE, "");
12954     },
12955     
12956     /**
12957      * Strips all HTML tags to sort on text only - Case insensitive
12958      * @param {Mixed} s The value being converted
12959      * @return {String} The comparison value
12960      */
12961     asUCText : function(s){
12962         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12963     },
12964     
12965     /**
12966      * Case insensitive string
12967      * @param {Mixed} s The value being converted
12968      * @return {String} The comparison value
12969      */
12970     asUCString : function(s) {
12971         return String(s).toUpperCase();
12972     },
12973     
12974     /**
12975      * Date sorting
12976      * @param {Mixed} s The value being converted
12977      * @return {Number} The comparison value
12978      */
12979     asDate : function(s) {
12980         if(!s){
12981             return 0;
12982         }
12983         if(s instanceof Date){
12984             return s.getTime();
12985         }
12986         return Date.parse(String(s));
12987     },
12988     
12989     /**
12990      * Float sorting
12991      * @param {Mixed} s The value being converted
12992      * @return {Float} The comparison value
12993      */
12994     asFloat : function(s) {
12995         var val = parseFloat(String(s).replace(/,/g, ""));
12996         if(isNaN(val)) {
12997             val = 0;
12998         }
12999         return val;
13000     },
13001     
13002     /**
13003      * Integer sorting
13004      * @param {Mixed} s The value being converted
13005      * @return {Number} The comparison value
13006      */
13007     asInt : function(s) {
13008         var val = parseInt(String(s).replace(/,/g, ""));
13009         if(isNaN(val)) {
13010             val = 0;
13011         }
13012         return val;
13013     }
13014 };/*
13015  * Based on:
13016  * Ext JS Library 1.1.1
13017  * Copyright(c) 2006-2007, Ext JS, LLC.
13018  *
13019  * Originally Released Under LGPL - original licence link has changed is not relivant.
13020  *
13021  * Fork - LGPL
13022  * <script type="text/javascript">
13023  */
13024
13025 /**
13026 * @class Roo.data.Record
13027  * Instances of this class encapsulate both record <em>definition</em> information, and record
13028  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13029  * to access Records cached in an {@link Roo.data.Store} object.<br>
13030  * <p>
13031  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13032  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13033  * objects.<br>
13034  * <p>
13035  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13036  * @constructor
13037  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13038  * {@link #create}. The parameters are the same.
13039  * @param {Array} data An associative Array of data values keyed by the field name.
13040  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13041  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13042  * not specified an integer id is generated.
13043  */
13044 Roo.data.Record = function(data, id){
13045     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13046     this.data = data;
13047 };
13048
13049 /**
13050  * Generate a constructor for a specific record layout.
13051  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13052  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13053  * Each field definition object may contain the following properties: <ul>
13054  * <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,
13055  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13056  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13057  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13058  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13059  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13060  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13061  * this may be omitted.</p></li>
13062  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13063  * <ul><li>auto (Default, implies no conversion)</li>
13064  * <li>string</li>
13065  * <li>int</li>
13066  * <li>float</li>
13067  * <li>boolean</li>
13068  * <li>date</li></ul></p></li>
13069  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13070  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13071  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13072  * by the Reader into an object that will be stored in the Record. It is passed the
13073  * following parameters:<ul>
13074  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13075  * </ul></p></li>
13076  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13077  * </ul>
13078  * <br>usage:<br><pre><code>
13079 var TopicRecord = Roo.data.Record.create(
13080     {name: 'title', mapping: 'topic_title'},
13081     {name: 'author', mapping: 'username'},
13082     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13083     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13084     {name: 'lastPoster', mapping: 'user2'},
13085     {name: 'excerpt', mapping: 'post_text'}
13086 );
13087
13088 var myNewRecord = new TopicRecord({
13089     title: 'Do my job please',
13090     author: 'noobie',
13091     totalPosts: 1,
13092     lastPost: new Date(),
13093     lastPoster: 'Animal',
13094     excerpt: 'No way dude!'
13095 });
13096 myStore.add(myNewRecord);
13097 </code></pre>
13098  * @method create
13099  * @static
13100  */
13101 Roo.data.Record.create = function(o){
13102     var f = function(){
13103         f.superclass.constructor.apply(this, arguments);
13104     };
13105     Roo.extend(f, Roo.data.Record);
13106     var p = f.prototype;
13107     p.fields = new Roo.util.MixedCollection(false, function(field){
13108         return field.name;
13109     });
13110     for(var i = 0, len = o.length; i < len; i++){
13111         p.fields.add(new Roo.data.Field(o[i]));
13112     }
13113     f.getField = function(name){
13114         return p.fields.get(name);  
13115     };
13116     return f;
13117 };
13118
13119 Roo.data.Record.AUTO_ID = 1000;
13120 Roo.data.Record.EDIT = 'edit';
13121 Roo.data.Record.REJECT = 'reject';
13122 Roo.data.Record.COMMIT = 'commit';
13123
13124 Roo.data.Record.prototype = {
13125     /**
13126      * Readonly flag - true if this record has been modified.
13127      * @type Boolean
13128      */
13129     dirty : false,
13130     editing : false,
13131     error: null,
13132     modified: null,
13133
13134     // private
13135     join : function(store){
13136         this.store = store;
13137     },
13138
13139     /**
13140      * Set the named field to the specified value.
13141      * @param {String} name The name of the field to set.
13142      * @param {Object} value The value to set the field to.
13143      */
13144     set : function(name, value){
13145         if(this.data[name] == value){
13146             return;
13147         }
13148         this.dirty = true;
13149         if(!this.modified){
13150             this.modified = {};
13151         }
13152         if(typeof this.modified[name] == 'undefined'){
13153             this.modified[name] = this.data[name];
13154         }
13155         this.data[name] = value;
13156         if(!this.editing && this.store){
13157             this.store.afterEdit(this);
13158         }       
13159     },
13160
13161     /**
13162      * Get the value of the named field.
13163      * @param {String} name The name of the field to get the value of.
13164      * @return {Object} The value of the field.
13165      */
13166     get : function(name){
13167         return this.data[name]; 
13168     },
13169
13170     // private
13171     beginEdit : function(){
13172         this.editing = true;
13173         this.modified = {}; 
13174     },
13175
13176     // private
13177     cancelEdit : function(){
13178         this.editing = false;
13179         delete this.modified;
13180     },
13181
13182     // private
13183     endEdit : function(){
13184         this.editing = false;
13185         if(this.dirty && this.store){
13186             this.store.afterEdit(this);
13187         }
13188     },
13189
13190     /**
13191      * Usually called by the {@link Roo.data.Store} which owns the Record.
13192      * Rejects all changes made to the Record since either creation, or the last commit operation.
13193      * Modified fields are reverted to their original values.
13194      * <p>
13195      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13196      * of reject operations.
13197      */
13198     reject : function(){
13199         var m = this.modified;
13200         for(var n in m){
13201             if(typeof m[n] != "function"){
13202                 this.data[n] = m[n];
13203             }
13204         }
13205         this.dirty = false;
13206         delete this.modified;
13207         this.editing = false;
13208         if(this.store){
13209             this.store.afterReject(this);
13210         }
13211     },
13212
13213     /**
13214      * Usually called by the {@link Roo.data.Store} which owns the Record.
13215      * Commits all changes made to the Record since either creation, or the last commit operation.
13216      * <p>
13217      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13218      * of commit operations.
13219      */
13220     commit : function(){
13221         this.dirty = false;
13222         delete this.modified;
13223         this.editing = false;
13224         if(this.store){
13225             this.store.afterCommit(this);
13226         }
13227     },
13228
13229     // private
13230     hasError : function(){
13231         return this.error != null;
13232     },
13233
13234     // private
13235     clearError : function(){
13236         this.error = null;
13237     },
13238
13239     /**
13240      * Creates a copy of this record.
13241      * @param {String} id (optional) A new record id if you don't want to use this record's id
13242      * @return {Record}
13243      */
13244     copy : function(newId) {
13245         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13246     }
13247 };/*
13248  * Based on:
13249  * Ext JS Library 1.1.1
13250  * Copyright(c) 2006-2007, Ext JS, LLC.
13251  *
13252  * Originally Released Under LGPL - original licence link has changed is not relivant.
13253  *
13254  * Fork - LGPL
13255  * <script type="text/javascript">
13256  */
13257
13258
13259
13260 /**
13261  * @class Roo.data.Store
13262  * @extends Roo.util.Observable
13263  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13264  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13265  * <p>
13266  * 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
13267  * has no knowledge of the format of the data returned by the Proxy.<br>
13268  * <p>
13269  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13270  * instances from the data object. These records are cached and made available through accessor functions.
13271  * @constructor
13272  * Creates a new Store.
13273  * @param {Object} config A config object containing the objects needed for the Store to access data,
13274  * and read the data into Records.
13275  */
13276 Roo.data.Store = function(config){
13277     this.data = new Roo.util.MixedCollection(false);
13278     this.data.getKey = function(o){
13279         return o.id;
13280     };
13281     this.baseParams = {};
13282     // private
13283     this.paramNames = {
13284         "start" : "start",
13285         "limit" : "limit",
13286         "sort" : "sort",
13287         "dir" : "dir",
13288         "multisort" : "_multisort"
13289     };
13290
13291     if(config && config.data){
13292         this.inlineData = config.data;
13293         delete config.data;
13294     }
13295
13296     Roo.apply(this, config);
13297     
13298     if(this.reader){ // reader passed
13299         this.reader = Roo.factory(this.reader, Roo.data);
13300         this.reader.xmodule = this.xmodule || false;
13301         if(!this.recordType){
13302             this.recordType = this.reader.recordType;
13303         }
13304         if(this.reader.onMetaChange){
13305             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13306         }
13307     }
13308
13309     if(this.recordType){
13310         this.fields = this.recordType.prototype.fields;
13311     }
13312     this.modified = [];
13313
13314     this.addEvents({
13315         /**
13316          * @event datachanged
13317          * Fires when the data cache has changed, and a widget which is using this Store
13318          * as a Record cache should refresh its view.
13319          * @param {Store} this
13320          */
13321         datachanged : true,
13322         /**
13323          * @event metachange
13324          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13325          * @param {Store} this
13326          * @param {Object} meta The JSON metadata
13327          */
13328         metachange : true,
13329         /**
13330          * @event add
13331          * Fires when Records have been added to the Store
13332          * @param {Store} this
13333          * @param {Roo.data.Record[]} records The array of Records added
13334          * @param {Number} index The index at which the record(s) were added
13335          */
13336         add : true,
13337         /**
13338          * @event remove
13339          * Fires when a Record has been removed from the Store
13340          * @param {Store} this
13341          * @param {Roo.data.Record} record The Record that was removed
13342          * @param {Number} index The index at which the record was removed
13343          */
13344         remove : true,
13345         /**
13346          * @event update
13347          * Fires when a Record has been updated
13348          * @param {Store} this
13349          * @param {Roo.data.Record} record The Record that was updated
13350          * @param {String} operation The update operation being performed.  Value may be one of:
13351          * <pre><code>
13352  Roo.data.Record.EDIT
13353  Roo.data.Record.REJECT
13354  Roo.data.Record.COMMIT
13355          * </code></pre>
13356          */
13357         update : true,
13358         /**
13359          * @event clear
13360          * Fires when the data cache has been cleared.
13361          * @param {Store} this
13362          */
13363         clear : true,
13364         /**
13365          * @event beforeload
13366          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13367          * the load action will be canceled.
13368          * @param {Store} this
13369          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13370          */
13371         beforeload : true,
13372         /**
13373          * @event beforeloadadd
13374          * Fires after a new set of Records has been loaded.
13375          * @param {Store} this
13376          * @param {Roo.data.Record[]} records The Records that were loaded
13377          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13378          */
13379         beforeloadadd : true,
13380         /**
13381          * @event load
13382          * Fires after a new set of Records has been loaded, before they are added to the store.
13383          * @param {Store} this
13384          * @param {Roo.data.Record[]} records The Records that were loaded
13385          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13386          * @params {Object} return from reader
13387          */
13388         load : true,
13389         /**
13390          * @event loadexception
13391          * Fires if an exception occurs in the Proxy during loading.
13392          * Called with the signature of the Proxy's "loadexception" event.
13393          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13394          * 
13395          * @param {Proxy} 
13396          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13397          * @param {Object} load options 
13398          * @param {Object} jsonData from your request (normally this contains the Exception)
13399          */
13400         loadexception : true
13401     });
13402     
13403     if(this.proxy){
13404         this.proxy = Roo.factory(this.proxy, Roo.data);
13405         this.proxy.xmodule = this.xmodule || false;
13406         this.relayEvents(this.proxy,  ["loadexception"]);
13407     }
13408     this.sortToggle = {};
13409     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13410
13411     Roo.data.Store.superclass.constructor.call(this);
13412
13413     if(this.inlineData){
13414         this.loadData(this.inlineData);
13415         delete this.inlineData;
13416     }
13417 };
13418
13419 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13420      /**
13421     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13422     * without a remote query - used by combo/forms at present.
13423     */
13424     
13425     /**
13426     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13427     */
13428     /**
13429     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13430     */
13431     /**
13432     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13433     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13434     */
13435     /**
13436     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13437     * on any HTTP request
13438     */
13439     /**
13440     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13441     */
13442     /**
13443     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13444     */
13445     multiSort: false,
13446     /**
13447     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13448     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13449     */
13450     remoteSort : false,
13451
13452     /**
13453     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13454      * loaded or when a record is removed. (defaults to false).
13455     */
13456     pruneModifiedRecords : false,
13457
13458     // private
13459     lastOptions : null,
13460
13461     /**
13462      * Add Records to the Store and fires the add event.
13463      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13464      */
13465     add : function(records){
13466         records = [].concat(records);
13467         for(var i = 0, len = records.length; i < len; i++){
13468             records[i].join(this);
13469         }
13470         var index = this.data.length;
13471         this.data.addAll(records);
13472         this.fireEvent("add", this, records, index);
13473     },
13474
13475     /**
13476      * Remove a Record from the Store and fires the remove event.
13477      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13478      */
13479     remove : function(record){
13480         var index = this.data.indexOf(record);
13481         this.data.removeAt(index);
13482  
13483         if(this.pruneModifiedRecords){
13484             this.modified.remove(record);
13485         }
13486         this.fireEvent("remove", this, record, index);
13487     },
13488
13489     /**
13490      * Remove all Records from the Store and fires the clear event.
13491      */
13492     removeAll : function(){
13493         this.data.clear();
13494         if(this.pruneModifiedRecords){
13495             this.modified = [];
13496         }
13497         this.fireEvent("clear", this);
13498     },
13499
13500     /**
13501      * Inserts Records to the Store at the given index and fires the add event.
13502      * @param {Number} index The start index at which to insert the passed Records.
13503      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13504      */
13505     insert : function(index, records){
13506         records = [].concat(records);
13507         for(var i = 0, len = records.length; i < len; i++){
13508             this.data.insert(index, records[i]);
13509             records[i].join(this);
13510         }
13511         this.fireEvent("add", this, records, index);
13512     },
13513
13514     /**
13515      * Get the index within the cache of the passed Record.
13516      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13517      * @return {Number} The index of the passed Record. Returns -1 if not found.
13518      */
13519     indexOf : function(record){
13520         return this.data.indexOf(record);
13521     },
13522
13523     /**
13524      * Get the index within the cache of the Record with the passed id.
13525      * @param {String} id The id of the Record to find.
13526      * @return {Number} The index of the Record. Returns -1 if not found.
13527      */
13528     indexOfId : function(id){
13529         return this.data.indexOfKey(id);
13530     },
13531
13532     /**
13533      * Get the Record with the specified id.
13534      * @param {String} id The id of the Record to find.
13535      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13536      */
13537     getById : function(id){
13538         return this.data.key(id);
13539     },
13540
13541     /**
13542      * Get the Record at the specified index.
13543      * @param {Number} index The index of the Record to find.
13544      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13545      */
13546     getAt : function(index){
13547         return this.data.itemAt(index);
13548     },
13549
13550     /**
13551      * Returns a range of Records between specified indices.
13552      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13553      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13554      * @return {Roo.data.Record[]} An array of Records
13555      */
13556     getRange : function(start, end){
13557         return this.data.getRange(start, end);
13558     },
13559
13560     // private
13561     storeOptions : function(o){
13562         o = Roo.apply({}, o);
13563         delete o.callback;
13564         delete o.scope;
13565         this.lastOptions = o;
13566     },
13567
13568     /**
13569      * Loads the Record cache from the configured Proxy using the configured Reader.
13570      * <p>
13571      * If using remote paging, then the first load call must specify the <em>start</em>
13572      * and <em>limit</em> properties in the options.params property to establish the initial
13573      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13574      * <p>
13575      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13576      * and this call will return before the new data has been loaded. Perform any post-processing
13577      * in a callback function, or in a "load" event handler.</strong>
13578      * <p>
13579      * @param {Object} options An object containing properties which control loading options:<ul>
13580      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13581      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13582      * passed the following arguments:<ul>
13583      * <li>r : Roo.data.Record[]</li>
13584      * <li>options: Options object from the load call</li>
13585      * <li>success: Boolean success indicator</li></ul></li>
13586      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13587      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13588      * </ul>
13589      */
13590     load : function(options){
13591         options = options || {};
13592         if(this.fireEvent("beforeload", this, options) !== false){
13593             this.storeOptions(options);
13594             var p = Roo.apply(options.params || {}, this.baseParams);
13595             // if meta was not loaded from remote source.. try requesting it.
13596             if (!this.reader.metaFromRemote) {
13597                 p._requestMeta = 1;
13598             }
13599             if(this.sortInfo && this.remoteSort){
13600                 var pn = this.paramNames;
13601                 p[pn["sort"]] = this.sortInfo.field;
13602                 p[pn["dir"]] = this.sortInfo.direction;
13603             }
13604             if (this.multiSort) {
13605                 var pn = this.paramNames;
13606                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13607             }
13608             
13609             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13610         }
13611     },
13612
13613     /**
13614      * Reloads the Record cache from the configured Proxy using the configured Reader and
13615      * the options from the last load operation performed.
13616      * @param {Object} options (optional) An object containing properties which may override the options
13617      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13618      * the most recently used options are reused).
13619      */
13620     reload : function(options){
13621         this.load(Roo.applyIf(options||{}, this.lastOptions));
13622     },
13623
13624     // private
13625     // Called as a callback by the Reader during a load operation.
13626     loadRecords : function(o, options, success){
13627         if(!o || success === false){
13628             if(success !== false){
13629                 this.fireEvent("load", this, [], options, o);
13630             }
13631             if(options.callback){
13632                 options.callback.call(options.scope || this, [], options, false);
13633             }
13634             return;
13635         }
13636         // if data returned failure - throw an exception.
13637         if (o.success === false) {
13638             // show a message if no listener is registered.
13639             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13640                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13641             }
13642             // loadmask wil be hooked into this..
13643             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13644             return;
13645         }
13646         var r = o.records, t = o.totalRecords || r.length;
13647         
13648         this.fireEvent("beforeloadadd", this, r, options, o);
13649         
13650         if(!options || options.add !== true){
13651             if(this.pruneModifiedRecords){
13652                 this.modified = [];
13653             }
13654             for(var i = 0, len = r.length; i < len; i++){
13655                 r[i].join(this);
13656             }
13657             if(this.snapshot){
13658                 this.data = this.snapshot;
13659                 delete this.snapshot;
13660             }
13661             this.data.clear();
13662             this.data.addAll(r);
13663             this.totalLength = t;
13664             this.applySort();
13665             this.fireEvent("datachanged", this);
13666         }else{
13667             this.totalLength = Math.max(t, this.data.length+r.length);
13668             this.add(r);
13669         }
13670         
13671         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13672                 
13673             var e = new Roo.data.Record({});
13674
13675             e.set(this.parent.displayField, this.parent.emptyTitle);
13676             e.set(this.parent.valueField, '');
13677
13678             this.insert(0, e);
13679         }
13680             
13681         this.fireEvent("load", this, r, options, o);
13682         if(options.callback){
13683             options.callback.call(options.scope || this, r, options, true);
13684         }
13685     },
13686
13687
13688     /**
13689      * Loads data from a passed data block. A Reader which understands the format of the data
13690      * must have been configured in the constructor.
13691      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13692      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13693      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13694      */
13695     loadData : function(o, append){
13696         var r = this.reader.readRecords(o);
13697         this.loadRecords(r, {add: append}, true);
13698     },
13699     
13700      /**
13701      * using 'cn' the nested child reader read the child array into it's child stores.
13702      * @param {Object} rec The record with a 'children array
13703      */
13704     loadDataFromChildren : function(rec)
13705     {
13706         this.loadData(this.reader.toLoadData(rec));
13707     },
13708     
13709
13710     /**
13711      * Gets the number of cached records.
13712      * <p>
13713      * <em>If using paging, this may not be the total size of the dataset. If the data object
13714      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13715      * the data set size</em>
13716      */
13717     getCount : function(){
13718         return this.data.length || 0;
13719     },
13720
13721     /**
13722      * Gets the total number of records in the dataset as returned by the server.
13723      * <p>
13724      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13725      * the dataset size</em>
13726      */
13727     getTotalCount : function(){
13728         return this.totalLength || 0;
13729     },
13730
13731     /**
13732      * Returns the sort state of the Store as an object with two properties:
13733      * <pre><code>
13734  field {String} The name of the field by which the Records are sorted
13735  direction {String} The sort order, "ASC" or "DESC"
13736      * </code></pre>
13737      */
13738     getSortState : function(){
13739         return this.sortInfo;
13740     },
13741
13742     // private
13743     applySort : function(){
13744         if(this.sortInfo && !this.remoteSort){
13745             var s = this.sortInfo, f = s.field;
13746             var st = this.fields.get(f).sortType;
13747             var fn = function(r1, r2){
13748                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13749                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13750             };
13751             this.data.sort(s.direction, fn);
13752             if(this.snapshot && this.snapshot != this.data){
13753                 this.snapshot.sort(s.direction, fn);
13754             }
13755         }
13756     },
13757
13758     /**
13759      * Sets the default sort column and order to be used by the next load operation.
13760      * @param {String} fieldName The name of the field to sort by.
13761      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13762      */
13763     setDefaultSort : function(field, dir){
13764         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13765     },
13766
13767     /**
13768      * Sort the Records.
13769      * If remote sorting is used, the sort is performed on the server, and the cache is
13770      * reloaded. If local sorting is used, the cache is sorted internally.
13771      * @param {String} fieldName The name of the field to sort by.
13772      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13773      */
13774     sort : function(fieldName, dir){
13775         var f = this.fields.get(fieldName);
13776         if(!dir){
13777             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13778             
13779             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13780                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13781             }else{
13782                 dir = f.sortDir;
13783             }
13784         }
13785         this.sortToggle[f.name] = dir;
13786         this.sortInfo = {field: f.name, direction: dir};
13787         if(!this.remoteSort){
13788             this.applySort();
13789             this.fireEvent("datachanged", this);
13790         }else{
13791             this.load(this.lastOptions);
13792         }
13793     },
13794
13795     /**
13796      * Calls the specified function for each of the Records in the cache.
13797      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13798      * Returning <em>false</em> aborts and exits the iteration.
13799      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13800      */
13801     each : function(fn, scope){
13802         this.data.each(fn, scope);
13803     },
13804
13805     /**
13806      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13807      * (e.g., during paging).
13808      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13809      */
13810     getModifiedRecords : function(){
13811         return this.modified;
13812     },
13813
13814     // private
13815     createFilterFn : function(property, value, anyMatch){
13816         if(!value.exec){ // not a regex
13817             value = String(value);
13818             if(value.length == 0){
13819                 return false;
13820             }
13821             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13822         }
13823         return function(r){
13824             return value.test(r.data[property]);
13825         };
13826     },
13827
13828     /**
13829      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13830      * @param {String} property A field on your records
13831      * @param {Number} start The record index to start at (defaults to 0)
13832      * @param {Number} end The last record index to include (defaults to length - 1)
13833      * @return {Number} The sum
13834      */
13835     sum : function(property, start, end){
13836         var rs = this.data.items, v = 0;
13837         start = start || 0;
13838         end = (end || end === 0) ? end : rs.length-1;
13839
13840         for(var i = start; i <= end; i++){
13841             v += (rs[i].data[property] || 0);
13842         }
13843         return v;
13844     },
13845
13846     /**
13847      * Filter the records by a specified property.
13848      * @param {String} field A field on your records
13849      * @param {String/RegExp} value Either a string that the field
13850      * should start with or a RegExp to test against the field
13851      * @param {Boolean} anyMatch True to match any part not just the beginning
13852      */
13853     filter : function(property, value, anyMatch){
13854         var fn = this.createFilterFn(property, value, anyMatch);
13855         return fn ? this.filterBy(fn) : this.clearFilter();
13856     },
13857
13858     /**
13859      * Filter by a function. The specified function will be called with each
13860      * record in this data source. If the function returns true the record is included,
13861      * otherwise it is filtered.
13862      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13863      * @param {Object} scope (optional) The scope of the function (defaults to this)
13864      */
13865     filterBy : function(fn, scope){
13866         this.snapshot = this.snapshot || this.data;
13867         this.data = this.queryBy(fn, scope||this);
13868         this.fireEvent("datachanged", this);
13869     },
13870
13871     /**
13872      * Query the records by a specified property.
13873      * @param {String} field A field on your records
13874      * @param {String/RegExp} value Either a string that the field
13875      * should start with or a RegExp to test against the field
13876      * @param {Boolean} anyMatch True to match any part not just the beginning
13877      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13878      */
13879     query : function(property, value, anyMatch){
13880         var fn = this.createFilterFn(property, value, anyMatch);
13881         return fn ? this.queryBy(fn) : this.data.clone();
13882     },
13883
13884     /**
13885      * Query by a function. The specified function will be called with each
13886      * record in this data source. If the function returns true the record is included
13887      * in the results.
13888      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13889      * @param {Object} scope (optional) The scope of the function (defaults to this)
13890       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13891      **/
13892     queryBy : function(fn, scope){
13893         var data = this.snapshot || this.data;
13894         return data.filterBy(fn, scope||this);
13895     },
13896
13897     /**
13898      * Collects unique values for a particular dataIndex from this store.
13899      * @param {String} dataIndex The property to collect
13900      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13901      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13902      * @return {Array} An array of the unique values
13903      **/
13904     collect : function(dataIndex, allowNull, bypassFilter){
13905         var d = (bypassFilter === true && this.snapshot) ?
13906                 this.snapshot.items : this.data.items;
13907         var v, sv, r = [], l = {};
13908         for(var i = 0, len = d.length; i < len; i++){
13909             v = d[i].data[dataIndex];
13910             sv = String(v);
13911             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13912                 l[sv] = true;
13913                 r[r.length] = v;
13914             }
13915         }
13916         return r;
13917     },
13918
13919     /**
13920      * Revert to a view of the Record cache with no filtering applied.
13921      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13922      */
13923     clearFilter : function(suppressEvent){
13924         if(this.snapshot && this.snapshot != this.data){
13925             this.data = this.snapshot;
13926             delete this.snapshot;
13927             if(suppressEvent !== true){
13928                 this.fireEvent("datachanged", this);
13929             }
13930         }
13931     },
13932
13933     // private
13934     afterEdit : function(record){
13935         if(this.modified.indexOf(record) == -1){
13936             this.modified.push(record);
13937         }
13938         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13939     },
13940     
13941     // private
13942     afterReject : function(record){
13943         this.modified.remove(record);
13944         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13945     },
13946
13947     // private
13948     afterCommit : function(record){
13949         this.modified.remove(record);
13950         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13951     },
13952
13953     /**
13954      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13955      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13956      */
13957     commitChanges : function(){
13958         var m = this.modified.slice(0);
13959         this.modified = [];
13960         for(var i = 0, len = m.length; i < len; i++){
13961             m[i].commit();
13962         }
13963     },
13964
13965     /**
13966      * Cancel outstanding changes on all changed records.
13967      */
13968     rejectChanges : function(){
13969         var m = this.modified.slice(0);
13970         this.modified = [];
13971         for(var i = 0, len = m.length; i < len; i++){
13972             m[i].reject();
13973         }
13974     },
13975
13976     onMetaChange : function(meta, rtype, o){
13977         this.recordType = rtype;
13978         this.fields = rtype.prototype.fields;
13979         delete this.snapshot;
13980         this.sortInfo = meta.sortInfo || this.sortInfo;
13981         this.modified = [];
13982         this.fireEvent('metachange', this, this.reader.meta);
13983     },
13984     
13985     moveIndex : function(data, type)
13986     {
13987         var index = this.indexOf(data);
13988         
13989         var newIndex = index + type;
13990         
13991         this.remove(data);
13992         
13993         this.insert(newIndex, data);
13994         
13995     }
13996 });/*
13997  * Based on:
13998  * Ext JS Library 1.1.1
13999  * Copyright(c) 2006-2007, Ext JS, LLC.
14000  *
14001  * Originally Released Under LGPL - original licence link has changed is not relivant.
14002  *
14003  * Fork - LGPL
14004  * <script type="text/javascript">
14005  */
14006
14007 /**
14008  * @class Roo.data.SimpleStore
14009  * @extends Roo.data.Store
14010  * Small helper class to make creating Stores from Array data easier.
14011  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14012  * @cfg {Array} fields An array of field definition objects, or field name strings.
14013  * @cfg {Object} an existing reader (eg. copied from another store)
14014  * @cfg {Array} data The multi-dimensional array of data
14015  * @constructor
14016  * @param {Object} config
14017  */
14018 Roo.data.SimpleStore = function(config)
14019 {
14020     Roo.data.SimpleStore.superclass.constructor.call(this, {
14021         isLocal : true,
14022         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14023                 id: config.id
14024             },
14025             Roo.data.Record.create(config.fields)
14026         ),
14027         proxy : new Roo.data.MemoryProxy(config.data)
14028     });
14029     this.load();
14030 };
14031 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14032  * Based on:
14033  * Ext JS Library 1.1.1
14034  * Copyright(c) 2006-2007, Ext JS, LLC.
14035  *
14036  * Originally Released Under LGPL - original licence link has changed is not relivant.
14037  *
14038  * Fork - LGPL
14039  * <script type="text/javascript">
14040  */
14041
14042 /**
14043 /**
14044  * @extends Roo.data.Store
14045  * @class Roo.data.JsonStore
14046  * Small helper class to make creating Stores for JSON data easier. <br/>
14047 <pre><code>
14048 var store = new Roo.data.JsonStore({
14049     url: 'get-images.php',
14050     root: 'images',
14051     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14052 });
14053 </code></pre>
14054  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14055  * JsonReader and HttpProxy (unless inline data is provided).</b>
14056  * @cfg {Array} fields An array of field definition objects, or field name strings.
14057  * @constructor
14058  * @param {Object} config
14059  */
14060 Roo.data.JsonStore = function(c){
14061     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14062         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14063         reader: new Roo.data.JsonReader(c, c.fields)
14064     }));
14065 };
14066 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14067  * Based on:
14068  * Ext JS Library 1.1.1
14069  * Copyright(c) 2006-2007, Ext JS, LLC.
14070  *
14071  * Originally Released Under LGPL - original licence link has changed is not relivant.
14072  *
14073  * Fork - LGPL
14074  * <script type="text/javascript">
14075  */
14076
14077  
14078 Roo.data.Field = function(config){
14079     if(typeof config == "string"){
14080         config = {name: config};
14081     }
14082     Roo.apply(this, config);
14083     
14084     if(!this.type){
14085         this.type = "auto";
14086     }
14087     
14088     var st = Roo.data.SortTypes;
14089     // named sortTypes are supported, here we look them up
14090     if(typeof this.sortType == "string"){
14091         this.sortType = st[this.sortType];
14092     }
14093     
14094     // set default sortType for strings and dates
14095     if(!this.sortType){
14096         switch(this.type){
14097             case "string":
14098                 this.sortType = st.asUCString;
14099                 break;
14100             case "date":
14101                 this.sortType = st.asDate;
14102                 break;
14103             default:
14104                 this.sortType = st.none;
14105         }
14106     }
14107
14108     // define once
14109     var stripRe = /[\$,%]/g;
14110
14111     // prebuilt conversion function for this field, instead of
14112     // switching every time we're reading a value
14113     if(!this.convert){
14114         var cv, dateFormat = this.dateFormat;
14115         switch(this.type){
14116             case "":
14117             case "auto":
14118             case undefined:
14119                 cv = function(v){ return v; };
14120                 break;
14121             case "string":
14122                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14123                 break;
14124             case "int":
14125                 cv = function(v){
14126                     return v !== undefined && v !== null && v !== '' ?
14127                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14128                     };
14129                 break;
14130             case "float":
14131                 cv = function(v){
14132                     return v !== undefined && v !== null && v !== '' ?
14133                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14134                     };
14135                 break;
14136             case "bool":
14137             case "boolean":
14138                 cv = function(v){ return v === true || v === "true" || v == 1; };
14139                 break;
14140             case "date":
14141                 cv = function(v){
14142                     if(!v){
14143                         return '';
14144                     }
14145                     if(v instanceof Date){
14146                         return v;
14147                     }
14148                     if(dateFormat){
14149                         if(dateFormat == "timestamp"){
14150                             return new Date(v*1000);
14151                         }
14152                         return Date.parseDate(v, dateFormat);
14153                     }
14154                     var parsed = Date.parse(v);
14155                     return parsed ? new Date(parsed) : null;
14156                 };
14157              break;
14158             
14159         }
14160         this.convert = cv;
14161     }
14162 };
14163
14164 Roo.data.Field.prototype = {
14165     dateFormat: null,
14166     defaultValue: "",
14167     mapping: null,
14168     sortType : null,
14169     sortDir : "ASC"
14170 };/*
14171  * Based on:
14172  * Ext JS Library 1.1.1
14173  * Copyright(c) 2006-2007, Ext JS, LLC.
14174  *
14175  * Originally Released Under LGPL - original licence link has changed is not relivant.
14176  *
14177  * Fork - LGPL
14178  * <script type="text/javascript">
14179  */
14180  
14181 // Base class for reading structured data from a data source.  This class is intended to be
14182 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14183
14184 /**
14185  * @class Roo.data.DataReader
14186  * Base class for reading structured data from a data source.  This class is intended to be
14187  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14188  */
14189
14190 Roo.data.DataReader = function(meta, recordType){
14191     
14192     this.meta = meta;
14193     
14194     this.recordType = recordType instanceof Array ? 
14195         Roo.data.Record.create(recordType) : recordType;
14196 };
14197
14198 Roo.data.DataReader.prototype = {
14199     
14200     
14201     readerType : 'Data',
14202      /**
14203      * Create an empty record
14204      * @param {Object} data (optional) - overlay some values
14205      * @return {Roo.data.Record} record created.
14206      */
14207     newRow :  function(d) {
14208         var da =  {};
14209         this.recordType.prototype.fields.each(function(c) {
14210             switch( c.type) {
14211                 case 'int' : da[c.name] = 0; break;
14212                 case 'date' : da[c.name] = new Date(); break;
14213                 case 'float' : da[c.name] = 0.0; break;
14214                 case 'boolean' : da[c.name] = false; break;
14215                 default : da[c.name] = ""; break;
14216             }
14217             
14218         });
14219         return new this.recordType(Roo.apply(da, d));
14220     }
14221     
14222     
14223 };/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233
14234 /**
14235  * @class Roo.data.DataProxy
14236  * @extends Roo.data.Observable
14237  * This class is an abstract base class for implementations which provide retrieval of
14238  * unformatted data objects.<br>
14239  * <p>
14240  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14241  * (of the appropriate type which knows how to parse the data object) to provide a block of
14242  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14243  * <p>
14244  * Custom implementations must implement the load method as described in
14245  * {@link Roo.data.HttpProxy#load}.
14246  */
14247 Roo.data.DataProxy = function(){
14248     this.addEvents({
14249         /**
14250          * @event beforeload
14251          * Fires before a network request is made to retrieve a data object.
14252          * @param {Object} This DataProxy object.
14253          * @param {Object} params The params parameter to the load function.
14254          */
14255         beforeload : true,
14256         /**
14257          * @event load
14258          * Fires before the load method's callback is called.
14259          * @param {Object} This DataProxy object.
14260          * @param {Object} o The data object.
14261          * @param {Object} arg The callback argument object passed to the load function.
14262          */
14263         load : true,
14264         /**
14265          * @event loadexception
14266          * Fires if an Exception occurs during data retrieval.
14267          * @param {Object} This DataProxy object.
14268          * @param {Object} o The data object.
14269          * @param {Object} arg The callback argument object passed to the load function.
14270          * @param {Object} e The Exception.
14271          */
14272         loadexception : true
14273     });
14274     Roo.data.DataProxy.superclass.constructor.call(this);
14275 };
14276
14277 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14278
14279     /**
14280      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14281      */
14282 /*
14283  * Based on:
14284  * Ext JS Library 1.1.1
14285  * Copyright(c) 2006-2007, Ext JS, LLC.
14286  *
14287  * Originally Released Under LGPL - original licence link has changed is not relivant.
14288  *
14289  * Fork - LGPL
14290  * <script type="text/javascript">
14291  */
14292 /**
14293  * @class Roo.data.MemoryProxy
14294  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14295  * to the Reader when its load method is called.
14296  * @constructor
14297  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14298  */
14299 Roo.data.MemoryProxy = function(data){
14300     if (data.data) {
14301         data = data.data;
14302     }
14303     Roo.data.MemoryProxy.superclass.constructor.call(this);
14304     this.data = data;
14305 };
14306
14307 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14308     
14309     /**
14310      * Load data from the requested source (in this case an in-memory
14311      * data object passed to the constructor), read the data object into
14312      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14313      * process that block using the passed callback.
14314      * @param {Object} params This parameter is not used by the MemoryProxy class.
14315      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14316      * object into a block of Roo.data.Records.
14317      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14318      * The function must be passed <ul>
14319      * <li>The Record block object</li>
14320      * <li>The "arg" argument from the load function</li>
14321      * <li>A boolean success indicator</li>
14322      * </ul>
14323      * @param {Object} scope The scope in which to call the callback
14324      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14325      */
14326     load : function(params, reader, callback, scope, arg){
14327         params = params || {};
14328         var result;
14329         try {
14330             result = reader.readRecords(params.data ? params.data :this.data);
14331         }catch(e){
14332             this.fireEvent("loadexception", this, arg, null, e);
14333             callback.call(scope, null, arg, false);
14334             return;
14335         }
14336         callback.call(scope, result, arg, true);
14337     },
14338     
14339     // private
14340     update : function(params, records){
14341         
14342     }
14343 });/*
14344  * Based on:
14345  * Ext JS Library 1.1.1
14346  * Copyright(c) 2006-2007, Ext JS, LLC.
14347  *
14348  * Originally Released Under LGPL - original licence link has changed is not relivant.
14349  *
14350  * Fork - LGPL
14351  * <script type="text/javascript">
14352  */
14353 /**
14354  * @class Roo.data.HttpProxy
14355  * @extends Roo.data.DataProxy
14356  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14357  * configured to reference a certain URL.<br><br>
14358  * <p>
14359  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14360  * from which the running page was served.<br><br>
14361  * <p>
14362  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14363  * <p>
14364  * Be aware that to enable the browser to parse an XML document, the server must set
14365  * the Content-Type header in the HTTP response to "text/xml".
14366  * @constructor
14367  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14368  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14369  * will be used to make the request.
14370  */
14371 Roo.data.HttpProxy = function(conn){
14372     Roo.data.HttpProxy.superclass.constructor.call(this);
14373     // is conn a conn config or a real conn?
14374     this.conn = conn;
14375     this.useAjax = !conn || !conn.events;
14376   
14377 };
14378
14379 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14380     // thse are take from connection...
14381     
14382     /**
14383      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14384      */
14385     /**
14386      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14387      * extra parameters to each request made by this object. (defaults to undefined)
14388      */
14389     /**
14390      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14391      *  to each request made by this object. (defaults to undefined)
14392      */
14393     /**
14394      * @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)
14395      */
14396     /**
14397      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14398      */
14399      /**
14400      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14401      * @type Boolean
14402      */
14403   
14404
14405     /**
14406      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14407      * @type Boolean
14408      */
14409     /**
14410      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14411      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14412      * a finer-grained basis than the DataProxy events.
14413      */
14414     getConnection : function(){
14415         return this.useAjax ? Roo.Ajax : this.conn;
14416     },
14417
14418     /**
14419      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14420      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14421      * process that block using the passed callback.
14422      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14423      * for the request to the remote server.
14424      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14425      * object into a block of Roo.data.Records.
14426      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14427      * The function must be passed <ul>
14428      * <li>The Record block object</li>
14429      * <li>The "arg" argument from the load function</li>
14430      * <li>A boolean success indicator</li>
14431      * </ul>
14432      * @param {Object} scope The scope in which to call the callback
14433      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14434      */
14435     load : function(params, reader, callback, scope, arg){
14436         if(this.fireEvent("beforeload", this, params) !== false){
14437             var  o = {
14438                 params : params || {},
14439                 request: {
14440                     callback : callback,
14441                     scope : scope,
14442                     arg : arg
14443                 },
14444                 reader: reader,
14445                 callback : this.loadResponse,
14446                 scope: this
14447             };
14448             if(this.useAjax){
14449                 Roo.applyIf(o, this.conn);
14450                 if(this.activeRequest){
14451                     Roo.Ajax.abort(this.activeRequest);
14452                 }
14453                 this.activeRequest = Roo.Ajax.request(o);
14454             }else{
14455                 this.conn.request(o);
14456             }
14457         }else{
14458             callback.call(scope||this, null, arg, false);
14459         }
14460     },
14461
14462     // private
14463     loadResponse : function(o, success, response){
14464         delete this.activeRequest;
14465         if(!success){
14466             this.fireEvent("loadexception", this, o, response);
14467             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14468             return;
14469         }
14470         var result;
14471         try {
14472             result = o.reader.read(response);
14473         }catch(e){
14474             this.fireEvent("loadexception", this, o, response, e);
14475             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14476             return;
14477         }
14478         
14479         this.fireEvent("load", this, o, o.request.arg);
14480         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14481     },
14482
14483     // private
14484     update : function(dataSet){
14485
14486     },
14487
14488     // private
14489     updateResponse : function(dataSet){
14490
14491     }
14492 });/*
14493  * Based on:
14494  * Ext JS Library 1.1.1
14495  * Copyright(c) 2006-2007, Ext JS, LLC.
14496  *
14497  * Originally Released Under LGPL - original licence link has changed is not relivant.
14498  *
14499  * Fork - LGPL
14500  * <script type="text/javascript">
14501  */
14502
14503 /**
14504  * @class Roo.data.ScriptTagProxy
14505  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14506  * other than the originating domain of the running page.<br><br>
14507  * <p>
14508  * <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
14509  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14510  * <p>
14511  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14512  * source code that is used as the source inside a &lt;script> tag.<br><br>
14513  * <p>
14514  * In order for the browser to process the returned data, the server must wrap the data object
14515  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14516  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14517  * depending on whether the callback name was passed:
14518  * <p>
14519  * <pre><code>
14520 boolean scriptTag = false;
14521 String cb = request.getParameter("callback");
14522 if (cb != null) {
14523     scriptTag = true;
14524     response.setContentType("text/javascript");
14525 } else {
14526     response.setContentType("application/x-json");
14527 }
14528 Writer out = response.getWriter();
14529 if (scriptTag) {
14530     out.write(cb + "(");
14531 }
14532 out.print(dataBlock.toJsonString());
14533 if (scriptTag) {
14534     out.write(");");
14535 }
14536 </pre></code>
14537  *
14538  * @constructor
14539  * @param {Object} config A configuration object.
14540  */
14541 Roo.data.ScriptTagProxy = function(config){
14542     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14543     Roo.apply(this, config);
14544     this.head = document.getElementsByTagName("head")[0];
14545 };
14546
14547 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14548
14549 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14550     /**
14551      * @cfg {String} url The URL from which to request the data object.
14552      */
14553     /**
14554      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14555      */
14556     timeout : 30000,
14557     /**
14558      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14559      * the server the name of the callback function set up by the load call to process the returned data object.
14560      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14561      * javascript output which calls this named function passing the data object as its only parameter.
14562      */
14563     callbackParam : "callback",
14564     /**
14565      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14566      * name to the request.
14567      */
14568     nocache : true,
14569
14570     /**
14571      * Load data from the configured URL, read the data object into
14572      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14573      * process that block using the passed callback.
14574      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14575      * for the request to the remote server.
14576      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14577      * object into a block of Roo.data.Records.
14578      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14579      * The function must be passed <ul>
14580      * <li>The Record block object</li>
14581      * <li>The "arg" argument from the load function</li>
14582      * <li>A boolean success indicator</li>
14583      * </ul>
14584      * @param {Object} scope The scope in which to call the callback
14585      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14586      */
14587     load : function(params, reader, callback, scope, arg){
14588         if(this.fireEvent("beforeload", this, params) !== false){
14589
14590             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14591
14592             var url = this.url;
14593             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14594             if(this.nocache){
14595                 url += "&_dc=" + (new Date().getTime());
14596             }
14597             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14598             var trans = {
14599                 id : transId,
14600                 cb : "stcCallback"+transId,
14601                 scriptId : "stcScript"+transId,
14602                 params : params,
14603                 arg : arg,
14604                 url : url,
14605                 callback : callback,
14606                 scope : scope,
14607                 reader : reader
14608             };
14609             var conn = this;
14610
14611             window[trans.cb] = function(o){
14612                 conn.handleResponse(o, trans);
14613             };
14614
14615             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14616
14617             if(this.autoAbort !== false){
14618                 this.abort();
14619             }
14620
14621             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14622
14623             var script = document.createElement("script");
14624             script.setAttribute("src", url);
14625             script.setAttribute("type", "text/javascript");
14626             script.setAttribute("id", trans.scriptId);
14627             this.head.appendChild(script);
14628
14629             this.trans = trans;
14630         }else{
14631             callback.call(scope||this, null, arg, false);
14632         }
14633     },
14634
14635     // private
14636     isLoading : function(){
14637         return this.trans ? true : false;
14638     },
14639
14640     /**
14641      * Abort the current server request.
14642      */
14643     abort : function(){
14644         if(this.isLoading()){
14645             this.destroyTrans(this.trans);
14646         }
14647     },
14648
14649     // private
14650     destroyTrans : function(trans, isLoaded){
14651         this.head.removeChild(document.getElementById(trans.scriptId));
14652         clearTimeout(trans.timeoutId);
14653         if(isLoaded){
14654             window[trans.cb] = undefined;
14655             try{
14656                 delete window[trans.cb];
14657             }catch(e){}
14658         }else{
14659             // if hasn't been loaded, wait for load to remove it to prevent script error
14660             window[trans.cb] = function(){
14661                 window[trans.cb] = undefined;
14662                 try{
14663                     delete window[trans.cb];
14664                 }catch(e){}
14665             };
14666         }
14667     },
14668
14669     // private
14670     handleResponse : function(o, trans){
14671         this.trans = false;
14672         this.destroyTrans(trans, true);
14673         var result;
14674         try {
14675             result = trans.reader.readRecords(o);
14676         }catch(e){
14677             this.fireEvent("loadexception", this, o, trans.arg, e);
14678             trans.callback.call(trans.scope||window, null, trans.arg, false);
14679             return;
14680         }
14681         this.fireEvent("load", this, o, trans.arg);
14682         trans.callback.call(trans.scope||window, result, trans.arg, true);
14683     },
14684
14685     // private
14686     handleFailure : function(trans){
14687         this.trans = false;
14688         this.destroyTrans(trans, false);
14689         this.fireEvent("loadexception", this, null, trans.arg);
14690         trans.callback.call(trans.scope||window, null, trans.arg, false);
14691     }
14692 });/*
14693  * Based on:
14694  * Ext JS Library 1.1.1
14695  * Copyright(c) 2006-2007, Ext JS, LLC.
14696  *
14697  * Originally Released Under LGPL - original licence link has changed is not relivant.
14698  *
14699  * Fork - LGPL
14700  * <script type="text/javascript">
14701  */
14702
14703 /**
14704  * @class Roo.data.JsonReader
14705  * @extends Roo.data.DataReader
14706  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14707  * based on mappings in a provided Roo.data.Record constructor.
14708  * 
14709  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14710  * in the reply previously. 
14711  * 
14712  * <p>
14713  * Example code:
14714  * <pre><code>
14715 var RecordDef = Roo.data.Record.create([
14716     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14717     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14718 ]);
14719 var myReader = new Roo.data.JsonReader({
14720     totalProperty: "results",    // The property which contains the total dataset size (optional)
14721     root: "rows",                // The property which contains an Array of row objects
14722     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14723 }, RecordDef);
14724 </code></pre>
14725  * <p>
14726  * This would consume a JSON file like this:
14727  * <pre><code>
14728 { 'results': 2, 'rows': [
14729     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14730     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14731 }
14732 </code></pre>
14733  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14734  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14735  * paged from the remote server.
14736  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14737  * @cfg {String} root name of the property which contains the Array of row objects.
14738  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14739  * @cfg {Array} fields Array of field definition objects
14740  * @constructor
14741  * Create a new JsonReader
14742  * @param {Object} meta Metadata configuration options
14743  * @param {Object} recordType Either an Array of field definition objects,
14744  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14745  */
14746 Roo.data.JsonReader = function(meta, recordType){
14747     
14748     meta = meta || {};
14749     // set some defaults:
14750     Roo.applyIf(meta, {
14751         totalProperty: 'total',
14752         successProperty : 'success',
14753         root : 'data',
14754         id : 'id'
14755     });
14756     
14757     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14758 };
14759 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14760     
14761     readerType : 'Json',
14762     
14763     /**
14764      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14765      * Used by Store query builder to append _requestMeta to params.
14766      * 
14767      */
14768     metaFromRemote : false,
14769     /**
14770      * This method is only used by a DataProxy which has retrieved data from a remote server.
14771      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14772      * @return {Object} data A data block which is used by an Roo.data.Store object as
14773      * a cache of Roo.data.Records.
14774      */
14775     read : function(response){
14776         var json = response.responseText;
14777        
14778         var o = /* eval:var:o */ eval("("+json+")");
14779         if(!o) {
14780             throw {message: "JsonReader.read: Json object not found"};
14781         }
14782         
14783         if(o.metaData){
14784             
14785             delete this.ef;
14786             this.metaFromRemote = true;
14787             this.meta = o.metaData;
14788             this.recordType = Roo.data.Record.create(o.metaData.fields);
14789             this.onMetaChange(this.meta, this.recordType, o);
14790         }
14791         return this.readRecords(o);
14792     },
14793
14794     // private function a store will implement
14795     onMetaChange : function(meta, recordType, o){
14796
14797     },
14798
14799     /**
14800          * @ignore
14801          */
14802     simpleAccess: function(obj, subsc) {
14803         return obj[subsc];
14804     },
14805
14806         /**
14807          * @ignore
14808          */
14809     getJsonAccessor: function(){
14810         var re = /[\[\.]/;
14811         return function(expr) {
14812             try {
14813                 return(re.test(expr))
14814                     ? new Function("obj", "return obj." + expr)
14815                     : function(obj){
14816                         return obj[expr];
14817                     };
14818             } catch(e){}
14819             return Roo.emptyFn;
14820         };
14821     }(),
14822
14823     /**
14824      * Create a data block containing Roo.data.Records from an XML document.
14825      * @param {Object} o An object which contains an Array of row objects in the property specified
14826      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14827      * which contains the total size of the dataset.
14828      * @return {Object} data A data block which is used by an Roo.data.Store object as
14829      * a cache of Roo.data.Records.
14830      */
14831     readRecords : function(o){
14832         /**
14833          * After any data loads, the raw JSON data is available for further custom processing.
14834          * @type Object
14835          */
14836         this.o = o;
14837         var s = this.meta, Record = this.recordType,
14838             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14839
14840 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14841         if (!this.ef) {
14842             if(s.totalProperty) {
14843                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14844                 }
14845                 if(s.successProperty) {
14846                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14847                 }
14848                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14849                 if (s.id) {
14850                         var g = this.getJsonAccessor(s.id);
14851                         this.getId = function(rec) {
14852                                 var r = g(rec);  
14853                                 return (r === undefined || r === "") ? null : r;
14854                         };
14855                 } else {
14856                         this.getId = function(){return null;};
14857                 }
14858             this.ef = [];
14859             for(var jj = 0; jj < fl; jj++){
14860                 f = fi[jj];
14861                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14862                 this.ef[jj] = this.getJsonAccessor(map);
14863             }
14864         }
14865
14866         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14867         if(s.totalProperty){
14868             var vt = parseInt(this.getTotal(o), 10);
14869             if(!isNaN(vt)){
14870                 totalRecords = vt;
14871             }
14872         }
14873         if(s.successProperty){
14874             var vs = this.getSuccess(o);
14875             if(vs === false || vs === 'false'){
14876                 success = false;
14877             }
14878         }
14879         var records = [];
14880         for(var i = 0; i < c; i++){
14881                 var n = root[i];
14882             var values = {};
14883             var id = this.getId(n);
14884             for(var j = 0; j < fl; j++){
14885                 f = fi[j];
14886             var v = this.ef[j](n);
14887             if (!f.convert) {
14888                 Roo.log('missing convert for ' + f.name);
14889                 Roo.log(f);
14890                 continue;
14891             }
14892             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14893             }
14894             var record = new Record(values, id);
14895             record.json = n;
14896             records[i] = record;
14897         }
14898         return {
14899             raw : o,
14900             success : success,
14901             records : records,
14902             totalRecords : totalRecords
14903         };
14904     },
14905     // used when loading children.. @see loadDataFromChildren
14906     toLoadData: function(rec)
14907     {
14908         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14909         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14910         return { data : data, total : data.length };
14911         
14912     }
14913 });/*
14914  * Based on:
14915  * Ext JS Library 1.1.1
14916  * Copyright(c) 2006-2007, Ext JS, LLC.
14917  *
14918  * Originally Released Under LGPL - original licence link has changed is not relivant.
14919  *
14920  * Fork - LGPL
14921  * <script type="text/javascript">
14922  */
14923
14924 /**
14925  * @class Roo.data.ArrayReader
14926  * @extends Roo.data.DataReader
14927  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14928  * Each element of that Array represents a row of data fields. The
14929  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14930  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14931  * <p>
14932  * Example code:.
14933  * <pre><code>
14934 var RecordDef = Roo.data.Record.create([
14935     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14936     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14937 ]);
14938 var myReader = new Roo.data.ArrayReader({
14939     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14940 }, RecordDef);
14941 </code></pre>
14942  * <p>
14943  * This would consume an Array like this:
14944  * <pre><code>
14945 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14946   </code></pre>
14947  
14948  * @constructor
14949  * Create a new JsonReader
14950  * @param {Object} meta Metadata configuration options.
14951  * @param {Object|Array} recordType Either an Array of field definition objects
14952  * 
14953  * @cfg {Array} fields Array of field definition objects
14954  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14955  * as specified to {@link Roo.data.Record#create},
14956  * or an {@link Roo.data.Record} object
14957  *
14958  * 
14959  * created using {@link Roo.data.Record#create}.
14960  */
14961 Roo.data.ArrayReader = function(meta, recordType)
14962 {    
14963     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14964 };
14965
14966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14967     
14968       /**
14969      * Create a data block containing Roo.data.Records from an XML document.
14970      * @param {Object} o An Array of row objects which represents the dataset.
14971      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14972      * a cache of Roo.data.Records.
14973      */
14974     readRecords : function(o)
14975     {
14976         var sid = this.meta ? this.meta.id : null;
14977         var recordType = this.recordType, fields = recordType.prototype.fields;
14978         var records = [];
14979         var root = o;
14980         for(var i = 0; i < root.length; i++){
14981                 var n = root[i];
14982             var values = {};
14983             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14984             for(var j = 0, jlen = fields.length; j < jlen; j++){
14985                 var f = fields.items[j];
14986                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14987                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14988                 v = f.convert(v);
14989                 values[f.name] = v;
14990             }
14991             var record = new recordType(values, id);
14992             record.json = n;
14993             records[records.length] = record;
14994         }
14995         return {
14996             records : records,
14997             totalRecords : records.length
14998         };
14999     },
15000     // used when loading children.. @see loadDataFromChildren
15001     toLoadData: function(rec)
15002     {
15003         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15004         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15005         
15006     }
15007     
15008     
15009 });/*
15010  * - LGPL
15011  * * 
15012  */
15013
15014 /**
15015  * @class Roo.bootstrap.ComboBox
15016  * @extends Roo.bootstrap.TriggerField
15017  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15018  * @cfg {Boolean} append (true|false) default false
15019  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15020  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15021  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15022  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15023  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15024  * @cfg {Boolean} animate default true
15025  * @cfg {Boolean} emptyResultText only for touch device
15026  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15027  * @cfg {String} emptyTitle default ''
15028  * @cfg {Number} width fixed with? experimental
15029  * @constructor
15030  * Create a new ComboBox.
15031  * @param {Object} config Configuration options
15032  */
15033 Roo.bootstrap.ComboBox = function(config){
15034     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15035     this.addEvents({
15036         /**
15037          * @event expand
15038          * Fires when the dropdown list is expanded
15039         * @param {Roo.bootstrap.ComboBox} combo This combo box
15040         */
15041         'expand' : true,
15042         /**
15043          * @event collapse
15044          * Fires when the dropdown list is collapsed
15045         * @param {Roo.bootstrap.ComboBox} combo This combo box
15046         */
15047         'collapse' : true,
15048         /**
15049          * @event beforeselect
15050          * Fires before a list item is selected. Return false to cancel the selection.
15051         * @param {Roo.bootstrap.ComboBox} combo This combo box
15052         * @param {Roo.data.Record} record The data record returned from the underlying store
15053         * @param {Number} index The index of the selected item in the dropdown list
15054         */
15055         'beforeselect' : true,
15056         /**
15057          * @event select
15058          * Fires when a list item is selected
15059         * @param {Roo.bootstrap.ComboBox} combo This combo box
15060         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15061         * @param {Number} index The index of the selected item in the dropdown list
15062         */
15063         'select' : true,
15064         /**
15065          * @event beforequery
15066          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15067          * The event object passed has these properties:
15068         * @param {Roo.bootstrap.ComboBox} combo This combo box
15069         * @param {String} query The query
15070         * @param {Boolean} forceAll true to force "all" query
15071         * @param {Boolean} cancel true to cancel the query
15072         * @param {Object} e The query event object
15073         */
15074         'beforequery': true,
15075          /**
15076          * @event add
15077          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15078         * @param {Roo.bootstrap.ComboBox} combo This combo box
15079         */
15080         'add' : true,
15081         /**
15082          * @event edit
15083          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15084         * @param {Roo.bootstrap.ComboBox} combo This combo box
15085         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15086         */
15087         'edit' : true,
15088         /**
15089          * @event remove
15090          * Fires when the remove value from the combobox array
15091         * @param {Roo.bootstrap.ComboBox} combo This combo box
15092         */
15093         'remove' : true,
15094         /**
15095          * @event afterremove
15096          * Fires when the remove value from the combobox array
15097         * @param {Roo.bootstrap.ComboBox} combo This combo box
15098         */
15099         'afterremove' : true,
15100         /**
15101          * @event specialfilter
15102          * Fires when specialfilter
15103             * @param {Roo.bootstrap.ComboBox} combo This combo box
15104             */
15105         'specialfilter' : true,
15106         /**
15107          * @event tick
15108          * Fires when tick the element
15109             * @param {Roo.bootstrap.ComboBox} combo This combo box
15110             */
15111         'tick' : true,
15112         /**
15113          * @event touchviewdisplay
15114          * Fires when touch view require special display (default is using displayField)
15115             * @param {Roo.bootstrap.ComboBox} combo This combo box
15116             * @param {Object} cfg set html .
15117             */
15118         'touchviewdisplay' : true
15119         
15120     });
15121     
15122     this.item = [];
15123     this.tickItems = [];
15124     
15125     this.selectedIndex = -1;
15126     if(this.mode == 'local'){
15127         if(config.queryDelay === undefined){
15128             this.queryDelay = 10;
15129         }
15130         if(config.minChars === undefined){
15131             this.minChars = 0;
15132         }
15133     }
15134 };
15135
15136 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15137      
15138     /**
15139      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15140      * rendering into an Roo.Editor, defaults to false)
15141      */
15142     /**
15143      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15144      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15145      */
15146     /**
15147      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15148      */
15149     /**
15150      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15151      * the dropdown list (defaults to undefined, with no header element)
15152      */
15153
15154      /**
15155      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15156      */
15157      
15158      /**
15159      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15160      */
15161     listWidth: undefined,
15162     /**
15163      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15164      * mode = 'remote' or 'text' if mode = 'local')
15165      */
15166     displayField: undefined,
15167     
15168     /**
15169      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15170      * mode = 'remote' or 'value' if mode = 'local'). 
15171      * Note: use of a valueField requires the user make a selection
15172      * in order for a value to be mapped.
15173      */
15174     valueField: undefined,
15175     /**
15176      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15177      */
15178     modalTitle : '',
15179     
15180     /**
15181      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15182      * field's data value (defaults to the underlying DOM element's name)
15183      */
15184     hiddenName: undefined,
15185     /**
15186      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15187      */
15188     listClass: '',
15189     /**
15190      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15191      */
15192     selectedClass: 'active',
15193     
15194     /**
15195      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15196      */
15197     shadow:'sides',
15198     /**
15199      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15200      * anchor positions (defaults to 'tl-bl')
15201      */
15202     listAlign: 'tl-bl?',
15203     /**
15204      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15205      */
15206     maxHeight: 300,
15207     /**
15208      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15209      * query specified by the allQuery config option (defaults to 'query')
15210      */
15211     triggerAction: 'query',
15212     /**
15213      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15214      * (defaults to 4, does not apply if editable = false)
15215      */
15216     minChars : 4,
15217     /**
15218      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15219      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15220      */
15221     typeAhead: false,
15222     /**
15223      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15224      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15225      */
15226     queryDelay: 500,
15227     /**
15228      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15229      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15230      */
15231     pageSize: 0,
15232     /**
15233      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15234      * when editable = true (defaults to false)
15235      */
15236     selectOnFocus:false,
15237     /**
15238      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15239      */
15240     queryParam: 'query',
15241     /**
15242      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15243      * when mode = 'remote' (defaults to 'Loading...')
15244      */
15245     loadingText: 'Loading...',
15246     /**
15247      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15248      */
15249     resizable: false,
15250     /**
15251      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15252      */
15253     handleHeight : 8,
15254     /**
15255      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15256      * traditional select (defaults to true)
15257      */
15258     editable: true,
15259     /**
15260      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15261      */
15262     allQuery: '',
15263     /**
15264      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15265      */
15266     mode: 'remote',
15267     /**
15268      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15269      * listWidth has a higher value)
15270      */
15271     minListWidth : 70,
15272     /**
15273      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15274      * allow the user to set arbitrary text into the field (defaults to false)
15275      */
15276     forceSelection:false,
15277     /**
15278      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15279      * if typeAhead = true (defaults to 250)
15280      */
15281     typeAheadDelay : 250,
15282     /**
15283      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15284      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15285      */
15286     valueNotFoundText : undefined,
15287     /**
15288      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15289      */
15290     blockFocus : false,
15291     
15292     /**
15293      * @cfg {Boolean} disableClear Disable showing of clear button.
15294      */
15295     disableClear : false,
15296     /**
15297      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15298      */
15299     alwaysQuery : false,
15300     
15301     /**
15302      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15303      */
15304     multiple : false,
15305     
15306     /**
15307      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15308      */
15309     invalidClass : "has-warning",
15310     
15311     /**
15312      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15313      */
15314     validClass : "has-success",
15315     
15316     /**
15317      * @cfg {Boolean} specialFilter (true|false) special filter default false
15318      */
15319     specialFilter : false,
15320     
15321     /**
15322      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15323      */
15324     mobileTouchView : true,
15325     
15326     /**
15327      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15328      */
15329     useNativeIOS : false,
15330     
15331     /**
15332      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15333      */
15334     mobile_restrict_height : false,
15335     
15336     ios_options : false,
15337     
15338     //private
15339     addicon : false,
15340     editicon: false,
15341     
15342     page: 0,
15343     hasQuery: false,
15344     append: false,
15345     loadNext: false,
15346     autoFocus : true,
15347     tickable : false,
15348     btnPosition : 'right',
15349     triggerList : true,
15350     showToggleBtn : true,
15351     animate : true,
15352     emptyResultText: 'Empty',
15353     triggerText : 'Select',
15354     emptyTitle : '',
15355     width : false,
15356     
15357     // element that contains real text value.. (when hidden is used..)
15358     
15359     getAutoCreate : function()
15360     {   
15361         var cfg = false;
15362         //render
15363         /*
15364          * Render classic select for iso
15365          */
15366         
15367         if(Roo.isIOS && this.useNativeIOS){
15368             cfg = this.getAutoCreateNativeIOS();
15369             return cfg;
15370         }
15371         
15372         /*
15373          * Touch Devices
15374          */
15375         
15376         if(Roo.isTouch && this.mobileTouchView){
15377             cfg = this.getAutoCreateTouchView();
15378             return cfg;;
15379         }
15380         
15381         /*
15382          *  Normal ComboBox
15383          */
15384         if(!this.tickable){
15385             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15386             return cfg;
15387         }
15388         
15389         /*
15390          *  ComboBox with tickable selections
15391          */
15392              
15393         var align = this.labelAlign || this.parentLabelAlign();
15394         
15395         cfg = {
15396             cls : 'form-group roo-combobox-tickable' //input-group
15397         };
15398         
15399         var btn_text_select = '';
15400         var btn_text_done = '';
15401         var btn_text_cancel = '';
15402         
15403         if (this.btn_text_show) {
15404             btn_text_select = 'Select';
15405             btn_text_done = 'Done';
15406             btn_text_cancel = 'Cancel'; 
15407         }
15408         
15409         var buttons = {
15410             tag : 'div',
15411             cls : 'tickable-buttons',
15412             cn : [
15413                 {
15414                     tag : 'button',
15415                     type : 'button',
15416                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15417                     //html : this.triggerText
15418                     html: btn_text_select
15419                 },
15420                 {
15421                     tag : 'button',
15422                     type : 'button',
15423                     name : 'ok',
15424                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15425                     //html : 'Done'
15426                     html: btn_text_done
15427                 },
15428                 {
15429                     tag : 'button',
15430                     type : 'button',
15431                     name : 'cancel',
15432                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15433                     //html : 'Cancel'
15434                     html: btn_text_cancel
15435                 }
15436             ]
15437         };
15438         
15439         if(this.editable){
15440             buttons.cn.unshift({
15441                 tag: 'input',
15442                 cls: 'roo-select2-search-field-input'
15443             });
15444         }
15445         
15446         var _this = this;
15447         
15448         Roo.each(buttons.cn, function(c){
15449             if (_this.size) {
15450                 c.cls += ' btn-' + _this.size;
15451             }
15452
15453             if (_this.disabled) {
15454                 c.disabled = true;
15455             }
15456         });
15457         
15458         var box = {
15459             tag: 'div',
15460             style : 'display: contents',
15461             cn: [
15462                 {
15463                     tag: 'input',
15464                     type : 'hidden',
15465                     cls: 'form-hidden-field'
15466                 },
15467                 {
15468                     tag: 'ul',
15469                     cls: 'roo-select2-choices',
15470                     cn:[
15471                         {
15472                             tag: 'li',
15473                             cls: 'roo-select2-search-field',
15474                             cn: [
15475                                 buttons
15476                             ]
15477                         }
15478                     ]
15479                 }
15480             ]
15481         };
15482         
15483         var combobox = {
15484             cls: 'roo-select2-container input-group roo-select2-container-multi',
15485             cn: [
15486                 
15487                 box
15488 //                {
15489 //                    tag: 'ul',
15490 //                    cls: 'typeahead typeahead-long dropdown-menu',
15491 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15492 //                }
15493             ]
15494         };
15495         
15496         if(this.hasFeedback && !this.allowBlank){
15497             
15498             var feedback = {
15499                 tag: 'span',
15500                 cls: 'glyphicon form-control-feedback'
15501             };
15502
15503             combobox.cn.push(feedback);
15504         }
15505         
15506         
15507         
15508         var indicator = {
15509             tag : 'i',
15510             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15511             tooltip : 'This field is required'
15512         };
15513         if (Roo.bootstrap.version == 4) {
15514             indicator = {
15515                 tag : 'i',
15516                 style : 'display:none'
15517             };
15518         }
15519         if (align ==='left' && this.fieldLabel.length) {
15520             
15521             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15522             
15523             cfg.cn = [
15524                 indicator,
15525                 {
15526                     tag: 'label',
15527                     'for' :  id,
15528                     cls : 'control-label col-form-label',
15529                     html : this.fieldLabel
15530
15531                 },
15532                 {
15533                     cls : "", 
15534                     cn: [
15535                         combobox
15536                     ]
15537                 }
15538
15539             ];
15540             
15541             var labelCfg = cfg.cn[1];
15542             var contentCfg = cfg.cn[2];
15543             
15544
15545             if(this.indicatorpos == 'right'){
15546                 
15547                 cfg.cn = [
15548                     {
15549                         tag: 'label',
15550                         'for' :  id,
15551                         cls : 'control-label col-form-label',
15552                         cn : [
15553                             {
15554                                 tag : 'span',
15555                                 html : this.fieldLabel
15556                             },
15557                             indicator
15558                         ]
15559                     },
15560                     {
15561                         cls : "",
15562                         cn: [
15563                             combobox
15564                         ]
15565                     }
15566
15567                 ];
15568                 
15569                 
15570                 
15571                 labelCfg = cfg.cn[0];
15572                 contentCfg = cfg.cn[1];
15573             
15574             }
15575             
15576             if(this.labelWidth > 12){
15577                 labelCfg.style = "width: " + this.labelWidth + 'px';
15578             }
15579             if(this.width * 1 > 0){
15580                 contentCfg.style = "width: " + this.width + 'px';
15581             }
15582             if(this.labelWidth < 13 && this.labelmd == 0){
15583                 this.labelmd = this.labelWidth;
15584             }
15585             
15586             if(this.labellg > 0){
15587                 labelCfg.cls += ' col-lg-' + this.labellg;
15588                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15589             }
15590             
15591             if(this.labelmd > 0){
15592                 labelCfg.cls += ' col-md-' + this.labelmd;
15593                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15594             }
15595             
15596             if(this.labelsm > 0){
15597                 labelCfg.cls += ' col-sm-' + this.labelsm;
15598                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15599             }
15600             
15601             if(this.labelxs > 0){
15602                 labelCfg.cls += ' col-xs-' + this.labelxs;
15603                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15604             }
15605                 
15606                 
15607         } else if ( this.fieldLabel.length) {
15608 //                Roo.log(" label");
15609                  cfg.cn = [
15610                    indicator,
15611                     {
15612                         tag: 'label',
15613                         //cls : 'input-group-addon',
15614                         html : this.fieldLabel
15615                     },
15616                     combobox
15617                 ];
15618                 
15619                 if(this.indicatorpos == 'right'){
15620                     cfg.cn = [
15621                         {
15622                             tag: 'label',
15623                             //cls : 'input-group-addon',
15624                             html : this.fieldLabel
15625                         },
15626                         indicator,
15627                         combobox
15628                     ];
15629                     
15630                 }
15631
15632         } else {
15633             
15634 //                Roo.log(" no label && no align");
15635                 cfg = combobox
15636                      
15637                 
15638         }
15639          
15640         var settings=this;
15641         ['xs','sm','md','lg'].map(function(size){
15642             if (settings[size]) {
15643                 cfg.cls += ' col-' + size + '-' + settings[size];
15644             }
15645         });
15646         
15647         return cfg;
15648         
15649     },
15650     
15651     _initEventsCalled : false,
15652     
15653     // private
15654     initEvents: function()
15655     {   
15656         if (this._initEventsCalled) { // as we call render... prevent looping...
15657             return;
15658         }
15659         this._initEventsCalled = true;
15660         
15661         if (!this.store) {
15662             throw "can not find store for combo";
15663         }
15664         
15665         this.indicator = this.indicatorEl();
15666         
15667         this.store = Roo.factory(this.store, Roo.data);
15668         this.store.parent = this;
15669         
15670         // if we are building from html. then this element is so complex, that we can not really
15671         // use the rendered HTML.
15672         // so we have to trash and replace the previous code.
15673         if (Roo.XComponent.build_from_html) {
15674             // remove this element....
15675             var e = this.el.dom, k=0;
15676             while (e ) { e = e.previousSibling;  ++k;}
15677
15678             this.el.remove();
15679             
15680             this.el=false;
15681             this.rendered = false;
15682             
15683             this.render(this.parent().getChildContainer(true), k);
15684         }
15685         
15686         if(Roo.isIOS && this.useNativeIOS){
15687             this.initIOSView();
15688             return;
15689         }
15690         
15691         /*
15692          * Touch Devices
15693          */
15694         
15695         if(Roo.isTouch && this.mobileTouchView){
15696             this.initTouchView();
15697             return;
15698         }
15699         
15700         if(this.tickable){
15701             this.initTickableEvents();
15702             return;
15703         }
15704         
15705         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15706         
15707         if(this.hiddenName){
15708             
15709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15710             
15711             this.hiddenField.dom.value =
15712                 this.hiddenValue !== undefined ? this.hiddenValue :
15713                 this.value !== undefined ? this.value : '';
15714
15715             // prevent input submission
15716             this.el.dom.removeAttribute('name');
15717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15718              
15719              
15720         }
15721         //if(Roo.isGecko){
15722         //    this.el.dom.setAttribute('autocomplete', 'off');
15723         //}
15724         
15725         var cls = 'x-combo-list';
15726         
15727         //this.list = new Roo.Layer({
15728         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15729         //});
15730         
15731         var _this = this;
15732         
15733         (function(){
15734             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15735             _this.list.setWidth(lw);
15736         }).defer(100);
15737         
15738         this.list.on('mouseover', this.onViewOver, this);
15739         this.list.on('mousemove', this.onViewMove, this);
15740         this.list.on('scroll', this.onViewScroll, this);
15741         
15742         /*
15743         this.list.swallowEvent('mousewheel');
15744         this.assetHeight = 0;
15745
15746         if(this.title){
15747             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15748             this.assetHeight += this.header.getHeight();
15749         }
15750
15751         this.innerList = this.list.createChild({cls:cls+'-inner'});
15752         this.innerList.on('mouseover', this.onViewOver, this);
15753         this.innerList.on('mousemove', this.onViewMove, this);
15754         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15755         
15756         if(this.allowBlank && !this.pageSize && !this.disableClear){
15757             this.footer = this.list.createChild({cls:cls+'-ft'});
15758             this.pageTb = new Roo.Toolbar(this.footer);
15759            
15760         }
15761         if(this.pageSize){
15762             this.footer = this.list.createChild({cls:cls+'-ft'});
15763             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15764                     {pageSize: this.pageSize});
15765             
15766         }
15767         
15768         if (this.pageTb && this.allowBlank && !this.disableClear) {
15769             var _this = this;
15770             this.pageTb.add(new Roo.Toolbar.Fill(), {
15771                 cls: 'x-btn-icon x-btn-clear',
15772                 text: '&#160;',
15773                 handler: function()
15774                 {
15775                     _this.collapse();
15776                     _this.clearValue();
15777                     _this.onSelect(false, -1);
15778                 }
15779             });
15780         }
15781         if (this.footer) {
15782             this.assetHeight += this.footer.getHeight();
15783         }
15784         */
15785             
15786         if(!this.tpl){
15787             this.tpl = Roo.bootstrap.version == 4 ?
15788                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15789                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15790         }
15791
15792         this.view = new Roo.View(this.list, this.tpl, {
15793             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15794         });
15795         //this.view.wrapEl.setDisplayed(false);
15796         this.view.on('click', this.onViewClick, this);
15797         
15798         
15799         this.store.on('beforeload', this.onBeforeLoad, this);
15800         this.store.on('load', this.onLoad, this);
15801         this.store.on('loadexception', this.onLoadException, this);
15802         /*
15803         if(this.resizable){
15804             this.resizer = new Roo.Resizable(this.list,  {
15805                pinned:true, handles:'se'
15806             });
15807             this.resizer.on('resize', function(r, w, h){
15808                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15809                 this.listWidth = w;
15810                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15811                 this.restrictHeight();
15812             }, this);
15813             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15814         }
15815         */
15816         if(!this.editable){
15817             this.editable = true;
15818             this.setEditable(false);
15819         }
15820         
15821         /*
15822         
15823         if (typeof(this.events.add.listeners) != 'undefined') {
15824             
15825             this.addicon = this.wrap.createChild(
15826                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15827        
15828             this.addicon.on('click', function(e) {
15829                 this.fireEvent('add', this);
15830             }, this);
15831         }
15832         if (typeof(this.events.edit.listeners) != 'undefined') {
15833             
15834             this.editicon = this.wrap.createChild(
15835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15836             if (this.addicon) {
15837                 this.editicon.setStyle('margin-left', '40px');
15838             }
15839             this.editicon.on('click', function(e) {
15840                 
15841                 // we fire even  if inothing is selected..
15842                 this.fireEvent('edit', this, this.lastData );
15843                 
15844             }, this);
15845         }
15846         */
15847         
15848         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15849             "up" : function(e){
15850                 this.inKeyMode = true;
15851                 this.selectPrev();
15852             },
15853
15854             "down" : function(e){
15855                 if(!this.isExpanded()){
15856                     this.onTriggerClick();
15857                 }else{
15858                     this.inKeyMode = true;
15859                     this.selectNext();
15860                 }
15861             },
15862
15863             "enter" : function(e){
15864 //                this.onViewClick();
15865                 //return true;
15866                 this.collapse();
15867                 
15868                 if(this.fireEvent("specialkey", this, e)){
15869                     this.onViewClick(false);
15870                 }
15871                 
15872                 return true;
15873             },
15874
15875             "esc" : function(e){
15876                 this.collapse();
15877             },
15878
15879             "tab" : function(e){
15880                 this.collapse();
15881                 
15882                 if(this.fireEvent("specialkey", this, e)){
15883                     this.onViewClick(false);
15884                 }
15885                 
15886                 return true;
15887             },
15888
15889             scope : this,
15890
15891             doRelay : function(foo, bar, hname){
15892                 if(hname == 'down' || this.scope.isExpanded()){
15893                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15894                 }
15895                 return true;
15896             },
15897
15898             forceKeyDown: true
15899         });
15900         
15901         
15902         this.queryDelay = Math.max(this.queryDelay || 10,
15903                 this.mode == 'local' ? 10 : 250);
15904         
15905         
15906         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15907         
15908         if(this.typeAhead){
15909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15910         }
15911         if(this.editable !== false){
15912             this.inputEl().on("keyup", this.onKeyUp, this);
15913         }
15914         if(this.forceSelection){
15915             this.inputEl().on('blur', this.doForce, this);
15916         }
15917         
15918         if(this.multiple){
15919             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15920             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15921         }
15922     },
15923     
15924     initTickableEvents: function()
15925     {   
15926         this.createList();
15927         
15928         if(this.hiddenName){
15929             
15930             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15931             
15932             this.hiddenField.dom.value =
15933                 this.hiddenValue !== undefined ? this.hiddenValue :
15934                 this.value !== undefined ? this.value : '';
15935
15936             // prevent input submission
15937             this.el.dom.removeAttribute('name');
15938             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15939              
15940              
15941         }
15942         
15943 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15944         
15945         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15946         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15947         if(this.triggerList){
15948             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15949         }
15950          
15951         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15952         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15953         
15954         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15955         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15956         
15957         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15958         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15959         
15960         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15961         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15962         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15963         
15964         this.okBtn.hide();
15965         this.cancelBtn.hide();
15966         
15967         var _this = this;
15968         
15969         (function(){
15970             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15971             _this.list.setWidth(lw);
15972         }).defer(100);
15973         
15974         this.list.on('mouseover', this.onViewOver, this);
15975         this.list.on('mousemove', this.onViewMove, this);
15976         
15977         this.list.on('scroll', this.onViewScroll, this);
15978         
15979         if(!this.tpl){
15980             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15981                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15982         }
15983
15984         this.view = new Roo.View(this.list, this.tpl, {
15985             singleSelect:true,
15986             tickable:true,
15987             parent:this,
15988             store: this.store,
15989             selectedClass: this.selectedClass
15990         });
15991         
15992         //this.view.wrapEl.setDisplayed(false);
15993         this.view.on('click', this.onViewClick, this);
15994         
15995         
15996         
15997         this.store.on('beforeload', this.onBeforeLoad, this);
15998         this.store.on('load', this.onLoad, this);
15999         this.store.on('loadexception', this.onLoadException, this);
16000         
16001         if(this.editable){
16002             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16003                 "up" : function(e){
16004                     this.inKeyMode = true;
16005                     this.selectPrev();
16006                 },
16007
16008                 "down" : function(e){
16009                     this.inKeyMode = true;
16010                     this.selectNext();
16011                 },
16012
16013                 "enter" : function(e){
16014                     if(this.fireEvent("specialkey", this, e)){
16015                         this.onViewClick(false);
16016                     }
16017                     
16018                     return true;
16019                 },
16020
16021                 "esc" : function(e){
16022                     this.onTickableFooterButtonClick(e, false, false);
16023                 },
16024
16025                 "tab" : function(e){
16026                     this.fireEvent("specialkey", this, e);
16027                     
16028                     this.onTickableFooterButtonClick(e, false, false);
16029                     
16030                     return true;
16031                 },
16032
16033                 scope : this,
16034
16035                 doRelay : function(e, fn, key){
16036                     if(this.scope.isExpanded()){
16037                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16038                     }
16039                     return true;
16040                 },
16041
16042                 forceKeyDown: true
16043             });
16044         }
16045         
16046         this.queryDelay = Math.max(this.queryDelay || 10,
16047                 this.mode == 'local' ? 10 : 250);
16048         
16049         
16050         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16051         
16052         if(this.typeAhead){
16053             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16054         }
16055         
16056         if(this.editable !== false){
16057             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16058         }
16059         
16060         this.indicator = this.indicatorEl();
16061         
16062         if(this.indicator){
16063             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16064             this.indicator.hide();
16065         }
16066         
16067     },
16068
16069     onDestroy : function(){
16070         if(this.view){
16071             this.view.setStore(null);
16072             this.view.el.removeAllListeners();
16073             this.view.el.remove();
16074             this.view.purgeListeners();
16075         }
16076         if(this.list){
16077             this.list.dom.innerHTML  = '';
16078         }
16079         
16080         if(this.store){
16081             this.store.un('beforeload', this.onBeforeLoad, this);
16082             this.store.un('load', this.onLoad, this);
16083             this.store.un('loadexception', this.onLoadException, this);
16084         }
16085         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16086     },
16087
16088     // private
16089     fireKey : function(e){
16090         if(e.isNavKeyPress() && !this.list.isVisible()){
16091             this.fireEvent("specialkey", this, e);
16092         }
16093     },
16094
16095     // private
16096     onResize: function(w, h)
16097     {
16098         
16099         
16100 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16101 //        
16102 //        if(typeof w != 'number'){
16103 //            // we do not handle it!?!?
16104 //            return;
16105 //        }
16106 //        var tw = this.trigger.getWidth();
16107 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16108 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16109 //        var x = w - tw;
16110 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16111 //            
16112 //        //this.trigger.setStyle('left', x+'px');
16113 //        
16114 //        if(this.list && this.listWidth === undefined){
16115 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16116 //            this.list.setWidth(lw);
16117 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16118 //        }
16119         
16120     
16121         
16122     },
16123
16124     /**
16125      * Allow or prevent the user from directly editing the field text.  If false is passed,
16126      * the user will only be able to select from the items defined in the dropdown list.  This method
16127      * is the runtime equivalent of setting the 'editable' config option at config time.
16128      * @param {Boolean} value True to allow the user to directly edit the field text
16129      */
16130     setEditable : function(value){
16131         if(value == this.editable){
16132             return;
16133         }
16134         this.editable = value;
16135         if(!value){
16136             this.inputEl().dom.setAttribute('readOnly', true);
16137             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16138             this.inputEl().addClass('x-combo-noedit');
16139         }else{
16140             this.inputEl().dom.setAttribute('readOnly', false);
16141             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16142             this.inputEl().removeClass('x-combo-noedit');
16143         }
16144     },
16145
16146     // private
16147     
16148     onBeforeLoad : function(combo,opts){
16149         if(!this.hasFocus){
16150             return;
16151         }
16152          if (!opts.add) {
16153             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16154          }
16155         this.restrictHeight();
16156         this.selectedIndex = -1;
16157     },
16158
16159     // private
16160     onLoad : function(){
16161         
16162         this.hasQuery = false;
16163         
16164         if(!this.hasFocus){
16165             return;
16166         }
16167         
16168         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16169             this.loading.hide();
16170         }
16171         
16172         if(this.store.getCount() > 0){
16173             
16174             this.expand();
16175             this.restrictHeight();
16176             if(this.lastQuery == this.allQuery){
16177                 if(this.editable && !this.tickable){
16178                     this.inputEl().dom.select();
16179                 }
16180                 
16181                 if(
16182                     !this.selectByValue(this.value, true) &&
16183                     this.autoFocus && 
16184                     (
16185                         !this.store.lastOptions ||
16186                         typeof(this.store.lastOptions.add) == 'undefined' || 
16187                         this.store.lastOptions.add != true
16188                     )
16189                 ){
16190                     this.select(0, true);
16191                 }
16192             }else{
16193                 if(this.autoFocus){
16194                     this.selectNext();
16195                 }
16196                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16197                     this.taTask.delay(this.typeAheadDelay);
16198                 }
16199             }
16200         }else{
16201             this.onEmptyResults();
16202         }
16203         
16204         //this.el.focus();
16205     },
16206     // private
16207     onLoadException : function()
16208     {
16209         this.hasQuery = false;
16210         
16211         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16212             this.loading.hide();
16213         }
16214         
16215         if(this.tickable && this.editable){
16216             return;
16217         }
16218         
16219         this.collapse();
16220         // only causes errors at present
16221         //Roo.log(this.store.reader.jsonData);
16222         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16223             // fixme
16224             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16225         //}
16226         
16227         
16228     },
16229     // private
16230     onTypeAhead : function(){
16231         if(this.store.getCount() > 0){
16232             var r = this.store.getAt(0);
16233             var newValue = r.data[this.displayField];
16234             var len = newValue.length;
16235             var selStart = this.getRawValue().length;
16236             
16237             if(selStart != len){
16238                 this.setRawValue(newValue);
16239                 this.selectText(selStart, newValue.length);
16240             }
16241         }
16242     },
16243
16244     // private
16245     onSelect : function(record, index){
16246         
16247         if(this.fireEvent('beforeselect', this, record, index) !== false){
16248         
16249             this.setFromData(index > -1 ? record.data : false);
16250             
16251             this.collapse();
16252             this.fireEvent('select', this, record, index);
16253         }
16254     },
16255
16256     /**
16257      * Returns the currently selected field value or empty string if no value is set.
16258      * @return {String} value The selected value
16259      */
16260     getValue : function()
16261     {
16262         if(Roo.isIOS && this.useNativeIOS){
16263             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16264         }
16265         
16266         if(this.multiple){
16267             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16268         }
16269         
16270         if(this.valueField){
16271             return typeof this.value != 'undefined' ? this.value : '';
16272         }else{
16273             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16274         }
16275     },
16276     
16277     getRawValue : function()
16278     {
16279         if(Roo.isIOS && this.useNativeIOS){
16280             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16281         }
16282         
16283         var v = this.inputEl().getValue();
16284         
16285         return v;
16286     },
16287
16288     /**
16289      * Clears any text/value currently set in the field
16290      */
16291     clearValue : function(){
16292         
16293         if(this.hiddenField){
16294             this.hiddenField.dom.value = '';
16295         }
16296         this.value = '';
16297         this.setRawValue('');
16298         this.lastSelectionText = '';
16299         this.lastData = false;
16300         
16301         var close = this.closeTriggerEl();
16302         
16303         if(close){
16304             close.hide();
16305         }
16306         
16307         this.validate();
16308         
16309     },
16310
16311     /**
16312      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16313      * will be displayed in the field.  If the value does not match the data value of an existing item,
16314      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16315      * Otherwise the field will be blank (although the value will still be set).
16316      * @param {String} value The value to match
16317      */
16318     setValue : function(v)
16319     {
16320         if(Roo.isIOS && this.useNativeIOS){
16321             this.setIOSValue(v);
16322             return;
16323         }
16324         
16325         if(this.multiple){
16326             this.syncValue();
16327             return;
16328         }
16329         
16330         var text = v;
16331         if(this.valueField){
16332             var r = this.findRecord(this.valueField, v);
16333             if(r){
16334                 text = r.data[this.displayField];
16335             }else if(this.valueNotFoundText !== undefined){
16336                 text = this.valueNotFoundText;
16337             }
16338         }
16339         this.lastSelectionText = text;
16340         if(this.hiddenField){
16341             this.hiddenField.dom.value = v;
16342         }
16343         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16344         this.value = v;
16345         
16346         var close = this.closeTriggerEl();
16347         
16348         if(close){
16349             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16350         }
16351         
16352         this.validate();
16353     },
16354     /**
16355      * @property {Object} the last set data for the element
16356      */
16357     
16358     lastData : false,
16359     /**
16360      * Sets the value of the field based on a object which is related to the record format for the store.
16361      * @param {Object} value the value to set as. or false on reset?
16362      */
16363     setFromData : function(o){
16364         
16365         if(this.multiple){
16366             this.addItem(o);
16367             return;
16368         }
16369             
16370         var dv = ''; // display value
16371         var vv = ''; // value value..
16372         this.lastData = o;
16373         if (this.displayField) {
16374             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16375         } else {
16376             // this is an error condition!!!
16377             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16378         }
16379         
16380         if(this.valueField){
16381             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16382         }
16383         
16384         var close = this.closeTriggerEl();
16385         
16386         if(close){
16387             if(dv.length || vv * 1 > 0){
16388                 close.show() ;
16389                 this.blockFocus=true;
16390             } else {
16391                 close.hide();
16392             }             
16393         }
16394         
16395         if(this.hiddenField){
16396             this.hiddenField.dom.value = vv;
16397             
16398             this.lastSelectionText = dv;
16399             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16400             this.value = vv;
16401             return;
16402         }
16403         // no hidden field.. - we store the value in 'value', but still display
16404         // display field!!!!
16405         this.lastSelectionText = dv;
16406         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16407         this.value = vv;
16408         
16409         
16410         
16411     },
16412     // private
16413     reset : function(){
16414         // overridden so that last data is reset..
16415         
16416         if(this.multiple){
16417             this.clearItem();
16418             return;
16419         }
16420         
16421         this.setValue(this.originalValue);
16422         //this.clearInvalid();
16423         this.lastData = false;
16424         if (this.view) {
16425             this.view.clearSelections();
16426         }
16427         
16428         this.validate();
16429     },
16430     // private
16431     findRecord : function(prop, value){
16432         var record;
16433         if(this.store.getCount() > 0){
16434             this.store.each(function(r){
16435                 if(r.data[prop] == value){
16436                     record = r;
16437                     return false;
16438                 }
16439                 return true;
16440             });
16441         }
16442         return record;
16443     },
16444     
16445     getName: function()
16446     {
16447         // returns hidden if it's set..
16448         if (!this.rendered) {return ''};
16449         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16450         
16451     },
16452     // private
16453     onViewMove : function(e, t){
16454         this.inKeyMode = false;
16455     },
16456
16457     // private
16458     onViewOver : function(e, t){
16459         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16460             return;
16461         }
16462         var item = this.view.findItemFromChild(t);
16463         
16464         if(item){
16465             var index = this.view.indexOf(item);
16466             this.select(index, false);
16467         }
16468     },
16469
16470     // private
16471     onViewClick : function(view, doFocus, el, e)
16472     {
16473         var index = this.view.getSelectedIndexes()[0];
16474         
16475         var r = this.store.getAt(index);
16476         
16477         if(this.tickable){
16478             
16479             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16480                 return;
16481             }
16482             
16483             var rm = false;
16484             var _this = this;
16485             
16486             Roo.each(this.tickItems, function(v,k){
16487                 
16488                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16489                     Roo.log(v);
16490                     _this.tickItems.splice(k, 1);
16491                     
16492                     if(typeof(e) == 'undefined' && view == false){
16493                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16494                     }
16495                     
16496                     rm = true;
16497                     return;
16498                 }
16499             });
16500             
16501             if(rm){
16502                 return;
16503             }
16504             
16505             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16506                 this.tickItems.push(r.data);
16507             }
16508             
16509             if(typeof(e) == 'undefined' && view == false){
16510                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16511             }
16512                     
16513             return;
16514         }
16515         
16516         if(r){
16517             this.onSelect(r, index);
16518         }
16519         if(doFocus !== false && !this.blockFocus){
16520             this.inputEl().focus();
16521         }
16522     },
16523
16524     // private
16525     restrictHeight : function(){
16526         //this.innerList.dom.style.height = '';
16527         //var inner = this.innerList.dom;
16528         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16529         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16530         //this.list.beginUpdate();
16531         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16532         this.list.alignTo(this.inputEl(), this.listAlign);
16533         this.list.alignTo(this.inputEl(), this.listAlign);
16534         //this.list.endUpdate();
16535     },
16536
16537     // private
16538     onEmptyResults : function(){
16539         
16540         if(this.tickable && this.editable){
16541             this.hasFocus = false;
16542             this.restrictHeight();
16543             return;
16544         }
16545         
16546         this.collapse();
16547     },
16548
16549     /**
16550      * Returns true if the dropdown list is expanded, else false.
16551      */
16552     isExpanded : function(){
16553         return this.list.isVisible();
16554     },
16555
16556     /**
16557      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16558      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16559      * @param {String} value The data value of the item to select
16560      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16561      * selected item if it is not currently in view (defaults to true)
16562      * @return {Boolean} True if the value matched an item in the list, else false
16563      */
16564     selectByValue : function(v, scrollIntoView){
16565         if(v !== undefined && v !== null){
16566             var r = this.findRecord(this.valueField || this.displayField, v);
16567             if(r){
16568                 this.select(this.store.indexOf(r), scrollIntoView);
16569                 return true;
16570             }
16571         }
16572         return false;
16573     },
16574
16575     /**
16576      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16577      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16578      * @param {Number} index The zero-based index of the list item to select
16579      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16580      * selected item if it is not currently in view (defaults to true)
16581      */
16582     select : function(index, scrollIntoView){
16583         this.selectedIndex = index;
16584         this.view.select(index);
16585         if(scrollIntoView !== false){
16586             var el = this.view.getNode(index);
16587             /*
16588              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16589              */
16590             if(el){
16591                 this.list.scrollChildIntoView(el, false);
16592             }
16593         }
16594     },
16595
16596     // private
16597     selectNext : function(){
16598         var ct = this.store.getCount();
16599         if(ct > 0){
16600             if(this.selectedIndex == -1){
16601                 this.select(0);
16602             }else if(this.selectedIndex < ct-1){
16603                 this.select(this.selectedIndex+1);
16604             }
16605         }
16606     },
16607
16608     // private
16609     selectPrev : function(){
16610         var ct = this.store.getCount();
16611         if(ct > 0){
16612             if(this.selectedIndex == -1){
16613                 this.select(0);
16614             }else if(this.selectedIndex != 0){
16615                 this.select(this.selectedIndex-1);
16616             }
16617         }
16618     },
16619
16620     // private
16621     onKeyUp : function(e){
16622         if(this.editable !== false && !e.isSpecialKey()){
16623             this.lastKey = e.getKey();
16624             this.dqTask.delay(this.queryDelay);
16625         }
16626     },
16627
16628     // private
16629     validateBlur : function(){
16630         return !this.list || !this.list.isVisible();   
16631     },
16632
16633     // private
16634     initQuery : function(){
16635         
16636         var v = this.getRawValue();
16637         
16638         if(this.tickable && this.editable){
16639             v = this.tickableInputEl().getValue();
16640         }
16641         
16642         this.doQuery(v);
16643     },
16644
16645     // private
16646     doForce : function(){
16647         if(this.inputEl().dom.value.length > 0){
16648             this.inputEl().dom.value =
16649                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16650              
16651         }
16652     },
16653
16654     /**
16655      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16656      * query allowing the query action to be canceled if needed.
16657      * @param {String} query The SQL query to execute
16658      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16659      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16660      * saved in the current store (defaults to false)
16661      */
16662     doQuery : function(q, forceAll){
16663         
16664         if(q === undefined || q === null){
16665             q = '';
16666         }
16667         var qe = {
16668             query: q,
16669             forceAll: forceAll,
16670             combo: this,
16671             cancel:false
16672         };
16673         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16674             return false;
16675         }
16676         q = qe.query;
16677         
16678         forceAll = qe.forceAll;
16679         if(forceAll === true || (q.length >= this.minChars)){
16680             
16681             this.hasQuery = true;
16682             
16683             if(this.lastQuery != q || this.alwaysQuery){
16684                 this.lastQuery = q;
16685                 if(this.mode == 'local'){
16686                     this.selectedIndex = -1;
16687                     if(forceAll){
16688                         this.store.clearFilter();
16689                     }else{
16690                         
16691                         if(this.specialFilter){
16692                             this.fireEvent('specialfilter', this);
16693                             this.onLoad();
16694                             return;
16695                         }
16696                         
16697                         this.store.filter(this.displayField, q);
16698                     }
16699                     
16700                     this.store.fireEvent("datachanged", this.store);
16701                     
16702                     this.onLoad();
16703                     
16704                     
16705                 }else{
16706                     
16707                     this.store.baseParams[this.queryParam] = q;
16708                     
16709                     var options = {params : this.getParams(q)};
16710                     
16711                     if(this.loadNext){
16712                         options.add = true;
16713                         options.params.start = this.page * this.pageSize;
16714                     }
16715                     
16716                     this.store.load(options);
16717                     
16718                     /*
16719                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16720                      *  we should expand the list on onLoad
16721                      *  so command out it
16722                      */
16723 //                    this.expand();
16724                 }
16725             }else{
16726                 this.selectedIndex = -1;
16727                 this.onLoad();   
16728             }
16729         }
16730         
16731         this.loadNext = false;
16732     },
16733     
16734     // private
16735     getParams : function(q){
16736         var p = {};
16737         //p[this.queryParam] = q;
16738         
16739         if(this.pageSize){
16740             p.start = 0;
16741             p.limit = this.pageSize;
16742         }
16743         return p;
16744     },
16745
16746     /**
16747      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16748      */
16749     collapse : function(){
16750         if(!this.isExpanded()){
16751             return;
16752         }
16753         
16754         this.list.hide();
16755         
16756         this.hasFocus = false;
16757         
16758         if(this.tickable){
16759             this.okBtn.hide();
16760             this.cancelBtn.hide();
16761             this.trigger.show();
16762             
16763             if(this.editable){
16764                 this.tickableInputEl().dom.value = '';
16765                 this.tickableInputEl().blur();
16766             }
16767             
16768         }
16769         
16770         Roo.get(document).un('mousedown', this.collapseIf, this);
16771         Roo.get(document).un('mousewheel', this.collapseIf, this);
16772         if (!this.editable) {
16773             Roo.get(document).un('keydown', this.listKeyPress, this);
16774         }
16775         this.fireEvent('collapse', this);
16776         
16777         this.validate();
16778     },
16779
16780     // private
16781     collapseIf : function(e){
16782         var in_combo  = e.within(this.el);
16783         var in_list =  e.within(this.list);
16784         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16785         
16786         if (in_combo || in_list || is_list) {
16787             //e.stopPropagation();
16788             return;
16789         }
16790         
16791         if(this.tickable){
16792             this.onTickableFooterButtonClick(e, false, false);
16793         }
16794
16795         this.collapse();
16796         
16797     },
16798
16799     /**
16800      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16801      */
16802     expand : function(){
16803        
16804         if(this.isExpanded() || !this.hasFocus){
16805             return;
16806         }
16807         
16808         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16809         this.list.setWidth(lw);
16810         
16811         Roo.log('expand');
16812         
16813         this.list.show();
16814         
16815         this.restrictHeight();
16816         
16817         if(this.tickable){
16818             
16819             this.tickItems = Roo.apply([], this.item);
16820             
16821             this.okBtn.show();
16822             this.cancelBtn.show();
16823             this.trigger.hide();
16824             
16825             if(this.editable){
16826                 this.tickableInputEl().focus();
16827             }
16828             
16829         }
16830         
16831         Roo.get(document).on('mousedown', this.collapseIf, this);
16832         Roo.get(document).on('mousewheel', this.collapseIf, this);
16833         if (!this.editable) {
16834             Roo.get(document).on('keydown', this.listKeyPress, this);
16835         }
16836         
16837         this.fireEvent('expand', this);
16838     },
16839
16840     // private
16841     // Implements the default empty TriggerField.onTriggerClick function
16842     onTriggerClick : function(e)
16843     {
16844         Roo.log('trigger click');
16845         
16846         if(this.disabled || !this.triggerList){
16847             return;
16848         }
16849         
16850         this.page = 0;
16851         this.loadNext = false;
16852         
16853         if(this.isExpanded()){
16854             this.collapse();
16855             if (!this.blockFocus) {
16856                 this.inputEl().focus();
16857             }
16858             
16859         }else {
16860             this.hasFocus = true;
16861             if(this.triggerAction == 'all') {
16862                 this.doQuery(this.allQuery, true);
16863             } else {
16864                 this.doQuery(this.getRawValue());
16865             }
16866             if (!this.blockFocus) {
16867                 this.inputEl().focus();
16868             }
16869         }
16870     },
16871     
16872     onTickableTriggerClick : function(e)
16873     {
16874         if(this.disabled){
16875             return;
16876         }
16877         
16878         this.page = 0;
16879         this.loadNext = false;
16880         this.hasFocus = true;
16881         
16882         if(this.triggerAction == 'all') {
16883             this.doQuery(this.allQuery, true);
16884         } else {
16885             this.doQuery(this.getRawValue());
16886         }
16887     },
16888     
16889     onSearchFieldClick : function(e)
16890     {
16891         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16892             this.onTickableFooterButtonClick(e, false, false);
16893             return;
16894         }
16895         
16896         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16897             return;
16898         }
16899         
16900         this.page = 0;
16901         this.loadNext = false;
16902         this.hasFocus = true;
16903         
16904         if(this.triggerAction == 'all') {
16905             this.doQuery(this.allQuery, true);
16906         } else {
16907             this.doQuery(this.getRawValue());
16908         }
16909     },
16910     
16911     listKeyPress : function(e)
16912     {
16913         //Roo.log('listkeypress');
16914         // scroll to first matching element based on key pres..
16915         if (e.isSpecialKey()) {
16916             return false;
16917         }
16918         var k = String.fromCharCode(e.getKey()).toUpperCase();
16919         //Roo.log(k);
16920         var match  = false;
16921         var csel = this.view.getSelectedNodes();
16922         var cselitem = false;
16923         if (csel.length) {
16924             var ix = this.view.indexOf(csel[0]);
16925             cselitem  = this.store.getAt(ix);
16926             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16927                 cselitem = false;
16928             }
16929             
16930         }
16931         
16932         this.store.each(function(v) { 
16933             if (cselitem) {
16934                 // start at existing selection.
16935                 if (cselitem.id == v.id) {
16936                     cselitem = false;
16937                 }
16938                 return true;
16939             }
16940                 
16941             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16942                 match = this.store.indexOf(v);
16943                 return false;
16944             }
16945             return true;
16946         }, this);
16947         
16948         if (match === false) {
16949             return true; // no more action?
16950         }
16951         // scroll to?
16952         this.view.select(match);
16953         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16954         sn.scrollIntoView(sn.dom.parentNode, false);
16955     },
16956     
16957     onViewScroll : function(e, t){
16958         
16959         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){
16960             return;
16961         }
16962         
16963         this.hasQuery = true;
16964         
16965         this.loading = this.list.select('.loading', true).first();
16966         
16967         if(this.loading === null){
16968             this.list.createChild({
16969                 tag: 'div',
16970                 cls: 'loading roo-select2-more-results roo-select2-active',
16971                 html: 'Loading more results...'
16972             });
16973             
16974             this.loading = this.list.select('.loading', true).first();
16975             
16976             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16977             
16978             this.loading.hide();
16979         }
16980         
16981         this.loading.show();
16982         
16983         var _combo = this;
16984         
16985         this.page++;
16986         this.loadNext = true;
16987         
16988         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16989         
16990         return;
16991     },
16992     
16993     addItem : function(o)
16994     {   
16995         var dv = ''; // display value
16996         
16997         if (this.displayField) {
16998             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16999         } else {
17000             // this is an error condition!!!
17001             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17002         }
17003         
17004         if(!dv.length){
17005             return;
17006         }
17007         
17008         var choice = this.choices.createChild({
17009             tag: 'li',
17010             cls: 'roo-select2-search-choice',
17011             cn: [
17012                 {
17013                     tag: 'div',
17014                     html: dv
17015                 },
17016                 {
17017                     tag: 'a',
17018                     href: '#',
17019                     cls: 'roo-select2-search-choice-close fa fa-times',
17020                     tabindex: '-1'
17021                 }
17022             ]
17023             
17024         }, this.searchField);
17025         
17026         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17027         
17028         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17029         
17030         this.item.push(o);
17031         
17032         this.lastData = o;
17033         
17034         this.syncValue();
17035         
17036         this.inputEl().dom.value = '';
17037         
17038         this.validate();
17039     },
17040     
17041     onRemoveItem : function(e, _self, o)
17042     {
17043         e.preventDefault();
17044         
17045         this.lastItem = Roo.apply([], this.item);
17046         
17047         var index = this.item.indexOf(o.data) * 1;
17048         
17049         if( index < 0){
17050             Roo.log('not this item?!');
17051             return;
17052         }
17053         
17054         this.item.splice(index, 1);
17055         o.item.remove();
17056         
17057         this.syncValue();
17058         
17059         this.fireEvent('remove', this, e);
17060         
17061         this.validate();
17062         
17063     },
17064     
17065     syncValue : function()
17066     {
17067         if(!this.item.length){
17068             this.clearValue();
17069             return;
17070         }
17071             
17072         var value = [];
17073         var _this = this;
17074         Roo.each(this.item, function(i){
17075             if(_this.valueField){
17076                 value.push(i[_this.valueField]);
17077                 return;
17078             }
17079
17080             value.push(i);
17081         });
17082
17083         this.value = value.join(',');
17084
17085         if(this.hiddenField){
17086             this.hiddenField.dom.value = this.value;
17087         }
17088         
17089         this.store.fireEvent("datachanged", this.store);
17090         
17091         this.validate();
17092     },
17093     
17094     clearItem : function()
17095     {
17096         if(!this.multiple){
17097             return;
17098         }
17099         
17100         this.item = [];
17101         
17102         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17103            c.remove();
17104         });
17105         
17106         this.syncValue();
17107         
17108         this.validate();
17109         
17110         if(this.tickable && !Roo.isTouch){
17111             this.view.refresh();
17112         }
17113     },
17114     
17115     inputEl: function ()
17116     {
17117         if(Roo.isIOS && this.useNativeIOS){
17118             return this.el.select('select.roo-ios-select', true).first();
17119         }
17120         
17121         if(Roo.isTouch && this.mobileTouchView){
17122             return this.el.select('input.form-control',true).first();
17123         }
17124         
17125         if(this.tickable){
17126             return this.searchField;
17127         }
17128         
17129         return this.el.select('input.form-control',true).first();
17130     },
17131     
17132     onTickableFooterButtonClick : function(e, btn, el)
17133     {
17134         e.preventDefault();
17135         
17136         this.lastItem = Roo.apply([], this.item);
17137         
17138         if(btn && btn.name == 'cancel'){
17139             this.tickItems = Roo.apply([], this.item);
17140             this.collapse();
17141             return;
17142         }
17143         
17144         this.clearItem();
17145         
17146         var _this = this;
17147         
17148         Roo.each(this.tickItems, function(o){
17149             _this.addItem(o);
17150         });
17151         
17152         this.collapse();
17153         
17154     },
17155     
17156     validate : function()
17157     {
17158         if(this.getVisibilityEl().hasClass('hidden')){
17159             return true;
17160         }
17161         
17162         var v = this.getRawValue();
17163         
17164         if(this.multiple){
17165             v = this.getValue();
17166         }
17167         
17168         if(this.disabled || this.allowBlank || v.length){
17169             this.markValid();
17170             return true;
17171         }
17172         
17173         this.markInvalid();
17174         return false;
17175     },
17176     
17177     tickableInputEl : function()
17178     {
17179         if(!this.tickable || !this.editable){
17180             return this.inputEl();
17181         }
17182         
17183         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17184     },
17185     
17186     
17187     getAutoCreateTouchView : function()
17188     {
17189         var id = Roo.id();
17190         
17191         var cfg = {
17192             cls: 'form-group' //input-group
17193         };
17194         
17195         var input =  {
17196             tag: 'input',
17197             id : id,
17198             type : this.inputType,
17199             cls : 'form-control x-combo-noedit',
17200             autocomplete: 'new-password',
17201             placeholder : this.placeholder || '',
17202             readonly : true
17203         };
17204         
17205         if (this.name) {
17206             input.name = this.name;
17207         }
17208         
17209         if (this.size) {
17210             input.cls += ' input-' + this.size;
17211         }
17212         
17213         if (this.disabled) {
17214             input.disabled = true;
17215         }
17216         
17217         var inputblock = {
17218             cls : 'roo-combobox-wrap',
17219             cn : [
17220                 input
17221             ]
17222         };
17223         
17224         if(this.before){
17225             inputblock.cls += ' input-group';
17226             
17227             inputblock.cn.unshift({
17228                 tag :'span',
17229                 cls : 'input-group-addon input-group-prepend input-group-text',
17230                 html : this.before
17231             });
17232         }
17233         
17234         if(this.removable && !this.multiple){
17235             inputblock.cls += ' roo-removable';
17236             
17237             inputblock.cn.push({
17238                 tag: 'button',
17239                 html : 'x',
17240                 cls : 'roo-combo-removable-btn close'
17241             });
17242         }
17243
17244         if(this.hasFeedback && !this.allowBlank){
17245             
17246             inputblock.cls += ' has-feedback';
17247             
17248             inputblock.cn.push({
17249                 tag: 'span',
17250                 cls: 'glyphicon form-control-feedback'
17251             });
17252             
17253         }
17254         
17255         if (this.after) {
17256             
17257             inputblock.cls += (this.before) ? '' : ' input-group';
17258             
17259             inputblock.cn.push({
17260                 tag :'span',
17261                 cls : 'input-group-addon input-group-append input-group-text',
17262                 html : this.after
17263             });
17264         }
17265
17266         
17267         var ibwrap = inputblock;
17268         
17269         if(this.multiple){
17270             ibwrap = {
17271                 tag: 'ul',
17272                 cls: 'roo-select2-choices',
17273                 cn:[
17274                     {
17275                         tag: 'li',
17276                         cls: 'roo-select2-search-field',
17277                         cn: [
17278
17279                             inputblock
17280                         ]
17281                     }
17282                 ]
17283             };
17284         
17285             
17286         }
17287         
17288         var combobox = {
17289             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17290             cn: [
17291                 {
17292                     tag: 'input',
17293                     type : 'hidden',
17294                     cls: 'form-hidden-field'
17295                 },
17296                 ibwrap
17297             ]
17298         };
17299         
17300         if(!this.multiple && this.showToggleBtn){
17301             
17302             var caret = {
17303                 cls: 'caret'
17304             };
17305             
17306             if (this.caret != false) {
17307                 caret = {
17308                      tag: 'i',
17309                      cls: 'fa fa-' + this.caret
17310                 };
17311                 
17312             }
17313             
17314             combobox.cn.push({
17315                 tag :'span',
17316                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17317                 cn : [
17318                     Roo.bootstrap.version == 3 ? caret : '',
17319                     {
17320                         tag: 'span',
17321                         cls: 'combobox-clear',
17322                         cn  : [
17323                             {
17324                                 tag : 'i',
17325                                 cls: 'icon-remove'
17326                             }
17327                         ]
17328                     }
17329                 ]
17330
17331             })
17332         }
17333         
17334         if(this.multiple){
17335             combobox.cls += ' roo-select2-container-multi';
17336         }
17337         
17338         var align = this.labelAlign || this.parentLabelAlign();
17339         
17340         if (align ==='left' && this.fieldLabel.length) {
17341
17342             cfg.cn = [
17343                 {
17344                    tag : 'i',
17345                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17346                    tooltip : 'This field is required'
17347                 },
17348                 {
17349                     tag: 'label',
17350                     cls : 'control-label col-form-label',
17351                     html : this.fieldLabel
17352
17353                 },
17354                 {
17355                     cls : 'roo-combobox-wrap ', 
17356                     cn: [
17357                         combobox
17358                     ]
17359                 }
17360             ];
17361             
17362             var labelCfg = cfg.cn[1];
17363             var contentCfg = cfg.cn[2];
17364             
17365
17366             if(this.indicatorpos == 'right'){
17367                 cfg.cn = [
17368                     {
17369                         tag: 'label',
17370                         'for' :  id,
17371                         cls : 'control-label col-form-label',
17372                         cn : [
17373                             {
17374                                 tag : 'span',
17375                                 html : this.fieldLabel
17376                             },
17377                             {
17378                                 tag : 'i',
17379                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17380                                 tooltip : 'This field is required'
17381                             }
17382                         ]
17383                     },
17384                     {
17385                         cls : "roo-combobox-wrap ",
17386                         cn: [
17387                             combobox
17388                         ]
17389                     }
17390
17391                 ];
17392                 
17393                 labelCfg = cfg.cn[0];
17394                 contentCfg = cfg.cn[1];
17395             }
17396             
17397            
17398             
17399             if(this.labelWidth > 12){
17400                 labelCfg.style = "width: " + this.labelWidth + 'px';
17401             }
17402            
17403             if(this.labelWidth < 13 && this.labelmd == 0){
17404                 this.labelmd = this.labelWidth;
17405             }
17406             
17407             if(this.labellg > 0){
17408                 labelCfg.cls += ' col-lg-' + this.labellg;
17409                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17410             }
17411             
17412             if(this.labelmd > 0){
17413                 labelCfg.cls += ' col-md-' + this.labelmd;
17414                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17415             }
17416             
17417             if(this.labelsm > 0){
17418                 labelCfg.cls += ' col-sm-' + this.labelsm;
17419                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17420             }
17421             
17422             if(this.labelxs > 0){
17423                 labelCfg.cls += ' col-xs-' + this.labelxs;
17424                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17425             }
17426                 
17427                 
17428         } else if ( this.fieldLabel.length) {
17429             cfg.cn = [
17430                 {
17431                    tag : 'i',
17432                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17433                    tooltip : 'This field is required'
17434                 },
17435                 {
17436                     tag: 'label',
17437                     cls : 'control-label',
17438                     html : this.fieldLabel
17439
17440                 },
17441                 {
17442                     cls : '', 
17443                     cn: [
17444                         combobox
17445                     ]
17446                 }
17447             ];
17448             
17449             if(this.indicatorpos == 'right'){
17450                 cfg.cn = [
17451                     {
17452                         tag: 'label',
17453                         cls : 'control-label',
17454                         html : this.fieldLabel,
17455                         cn : [
17456                             {
17457                                tag : 'i',
17458                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17459                                tooltip : 'This field is required'
17460                             }
17461                         ]
17462                     },
17463                     {
17464                         cls : '', 
17465                         cn: [
17466                             combobox
17467                         ]
17468                     }
17469                 ];
17470             }
17471         } else {
17472             cfg.cn = combobox;    
17473         }
17474         
17475         
17476         var settings = this;
17477         
17478         ['xs','sm','md','lg'].map(function(size){
17479             if (settings[size]) {
17480                 cfg.cls += ' col-' + size + '-' + settings[size];
17481             }
17482         });
17483         
17484         return cfg;
17485     },
17486     
17487     initTouchView : function()
17488     {
17489         this.renderTouchView();
17490         
17491         this.touchViewEl.on('scroll', function(){
17492             this.el.dom.scrollTop = 0;
17493         }, this);
17494         
17495         this.originalValue = this.getValue();
17496         
17497         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17498         
17499         this.inputEl().on("click", this.showTouchView, this);
17500         if (this.triggerEl) {
17501             this.triggerEl.on("click", this.showTouchView, this);
17502         }
17503         
17504         
17505         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17506         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17507         
17508         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17509         
17510         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17511         this.store.on('load', this.onTouchViewLoad, this);
17512         this.store.on('loadexception', this.onTouchViewLoadException, this);
17513         
17514         if(this.hiddenName){
17515             
17516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17517             
17518             this.hiddenField.dom.value =
17519                 this.hiddenValue !== undefined ? this.hiddenValue :
17520                 this.value !== undefined ? this.value : '';
17521         
17522             this.el.dom.removeAttribute('name');
17523             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17524         }
17525         
17526         if(this.multiple){
17527             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17528             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17529         }
17530         
17531         if(this.removable && !this.multiple){
17532             var close = this.closeTriggerEl();
17533             if(close){
17534                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17535                 close.on('click', this.removeBtnClick, this, close);
17536             }
17537         }
17538         /*
17539          * fix the bug in Safari iOS8
17540          */
17541         this.inputEl().on("focus", function(e){
17542             document.activeElement.blur();
17543         }, this);
17544         
17545         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17546         
17547         return;
17548         
17549         
17550     },
17551     
17552     renderTouchView : function()
17553     {
17554         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17555         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17556         
17557         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17558         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17559         
17560         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17561         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17562         this.touchViewBodyEl.setStyle('overflow', 'auto');
17563         
17564         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17565         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17566         
17567         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17568         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17569         
17570     },
17571     
17572     showTouchView : function()
17573     {
17574         if(this.disabled){
17575             return;
17576         }
17577         
17578         this.touchViewHeaderEl.hide();
17579
17580         if(this.modalTitle.length){
17581             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17582             this.touchViewHeaderEl.show();
17583         }
17584
17585         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17586         this.touchViewEl.show();
17587
17588         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17589         
17590         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17591         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17592
17593         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17594
17595         if(this.modalTitle.length){
17596             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17597         }
17598         
17599         this.touchViewBodyEl.setHeight(bodyHeight);
17600
17601         if(this.animate){
17602             var _this = this;
17603             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17604         }else{
17605             this.touchViewEl.addClass(['in','show']);
17606         }
17607         
17608         if(this._touchViewMask){
17609             Roo.get(document.body).addClass("x-body-masked");
17610             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17611             this._touchViewMask.setStyle('z-index', 10000);
17612             this._touchViewMask.addClass('show');
17613         }
17614         
17615         this.doTouchViewQuery();
17616         
17617     },
17618     
17619     hideTouchView : function()
17620     {
17621         this.touchViewEl.removeClass(['in','show']);
17622
17623         if(this.animate){
17624             var _this = this;
17625             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17626         }else{
17627             this.touchViewEl.setStyle('display', 'none');
17628         }
17629         
17630         if(this._touchViewMask){
17631             this._touchViewMask.removeClass('show');
17632             Roo.get(document.body).removeClass("x-body-masked");
17633         }
17634     },
17635     
17636     setTouchViewValue : function()
17637     {
17638         if(this.multiple){
17639             this.clearItem();
17640         
17641             var _this = this;
17642
17643             Roo.each(this.tickItems, function(o){
17644                 this.addItem(o);
17645             }, this);
17646         }
17647         
17648         this.hideTouchView();
17649     },
17650     
17651     doTouchViewQuery : function()
17652     {
17653         var qe = {
17654             query: '',
17655             forceAll: true,
17656             combo: this,
17657             cancel:false
17658         };
17659         
17660         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17661             return false;
17662         }
17663         
17664         if(!this.alwaysQuery || this.mode == 'local'){
17665             this.onTouchViewLoad();
17666             return;
17667         }
17668         
17669         this.store.load();
17670     },
17671     
17672     onTouchViewBeforeLoad : function(combo,opts)
17673     {
17674         return;
17675     },
17676
17677     // private
17678     onTouchViewLoad : function()
17679     {
17680         if(this.store.getCount() < 1){
17681             this.onTouchViewEmptyResults();
17682             return;
17683         }
17684         
17685         this.clearTouchView();
17686         
17687         var rawValue = this.getRawValue();
17688         
17689         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17690         
17691         this.tickItems = [];
17692         
17693         this.store.data.each(function(d, rowIndex){
17694             var row = this.touchViewListGroup.createChild(template);
17695             
17696             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17697                 row.addClass(d.data.cls);
17698             }
17699             
17700             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17701                 var cfg = {
17702                     data : d.data,
17703                     html : d.data[this.displayField]
17704                 };
17705                 
17706                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17707                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17708                 }
17709             }
17710             row.removeClass('selected');
17711             if(!this.multiple && this.valueField &&
17712                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17713             {
17714                 // radio buttons..
17715                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17716                 row.addClass('selected');
17717             }
17718             
17719             if(this.multiple && this.valueField &&
17720                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17721             {
17722                 
17723                 // checkboxes...
17724                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17725                 this.tickItems.push(d.data);
17726             }
17727             
17728             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17729             
17730         }, this);
17731         
17732         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17733         
17734         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17735
17736         if(this.modalTitle.length){
17737             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17738         }
17739
17740         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17741         
17742         if(this.mobile_restrict_height && listHeight < bodyHeight){
17743             this.touchViewBodyEl.setHeight(listHeight);
17744         }
17745         
17746         var _this = this;
17747         
17748         if(firstChecked && listHeight > bodyHeight){
17749             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17750         }
17751         
17752     },
17753     
17754     onTouchViewLoadException : function()
17755     {
17756         this.hideTouchView();
17757     },
17758     
17759     onTouchViewEmptyResults : function()
17760     {
17761         this.clearTouchView();
17762         
17763         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17764         
17765         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17766         
17767     },
17768     
17769     clearTouchView : function()
17770     {
17771         this.touchViewListGroup.dom.innerHTML = '';
17772     },
17773     
17774     onTouchViewClick : function(e, el, o)
17775     {
17776         e.preventDefault();
17777         
17778         var row = o.row;
17779         var rowIndex = o.rowIndex;
17780         
17781         var r = this.store.getAt(rowIndex);
17782         
17783         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17784             
17785             if(!this.multiple){
17786                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17787                     c.dom.removeAttribute('checked');
17788                 }, this);
17789
17790                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17791
17792                 this.setFromData(r.data);
17793
17794                 var close = this.closeTriggerEl();
17795
17796                 if(close){
17797                     close.show();
17798                 }
17799
17800                 this.hideTouchView();
17801
17802                 this.fireEvent('select', this, r, rowIndex);
17803
17804                 return;
17805             }
17806
17807             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17808                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17809                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17810                 return;
17811             }
17812
17813             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17814             this.addItem(r.data);
17815             this.tickItems.push(r.data);
17816         }
17817     },
17818     
17819     getAutoCreateNativeIOS : function()
17820     {
17821         var cfg = {
17822             cls: 'form-group' //input-group,
17823         };
17824         
17825         var combobox =  {
17826             tag: 'select',
17827             cls : 'roo-ios-select'
17828         };
17829         
17830         if (this.name) {
17831             combobox.name = this.name;
17832         }
17833         
17834         if (this.disabled) {
17835             combobox.disabled = true;
17836         }
17837         
17838         var settings = this;
17839         
17840         ['xs','sm','md','lg'].map(function(size){
17841             if (settings[size]) {
17842                 cfg.cls += ' col-' + size + '-' + settings[size];
17843             }
17844         });
17845         
17846         cfg.cn = combobox;
17847         
17848         return cfg;
17849         
17850     },
17851     
17852     initIOSView : function()
17853     {
17854         this.store.on('load', this.onIOSViewLoad, this);
17855         
17856         return;
17857     },
17858     
17859     onIOSViewLoad : function()
17860     {
17861         if(this.store.getCount() < 1){
17862             return;
17863         }
17864         
17865         this.clearIOSView();
17866         
17867         if(this.allowBlank) {
17868             
17869             var default_text = '-- SELECT --';
17870             
17871             if(this.placeholder.length){
17872                 default_text = this.placeholder;
17873             }
17874             
17875             if(this.emptyTitle.length){
17876                 default_text += ' - ' + this.emptyTitle + ' -';
17877             }
17878             
17879             var opt = this.inputEl().createChild({
17880                 tag: 'option',
17881                 value : 0,
17882                 html : default_text
17883             });
17884             
17885             var o = {};
17886             o[this.valueField] = 0;
17887             o[this.displayField] = default_text;
17888             
17889             this.ios_options.push({
17890                 data : o,
17891                 el : opt
17892             });
17893             
17894         }
17895         
17896         this.store.data.each(function(d, rowIndex){
17897             
17898             var html = '';
17899             
17900             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17901                 html = d.data[this.displayField];
17902             }
17903             
17904             var value = '';
17905             
17906             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17907                 value = d.data[this.valueField];
17908             }
17909             
17910             var option = {
17911                 tag: 'option',
17912                 value : value,
17913                 html : html
17914             };
17915             
17916             if(this.value == d.data[this.valueField]){
17917                 option['selected'] = true;
17918             }
17919             
17920             var opt = this.inputEl().createChild(option);
17921             
17922             this.ios_options.push({
17923                 data : d.data,
17924                 el : opt
17925             });
17926             
17927         }, this);
17928         
17929         this.inputEl().on('change', function(){
17930            this.fireEvent('select', this);
17931         }, this);
17932         
17933     },
17934     
17935     clearIOSView: function()
17936     {
17937         this.inputEl().dom.innerHTML = '';
17938         
17939         this.ios_options = [];
17940     },
17941     
17942     setIOSValue: function(v)
17943     {
17944         this.value = v;
17945         
17946         if(!this.ios_options){
17947             return;
17948         }
17949         
17950         Roo.each(this.ios_options, function(opts){
17951            
17952            opts.el.dom.removeAttribute('selected');
17953            
17954            if(opts.data[this.valueField] != v){
17955                return;
17956            }
17957            
17958            opts.el.dom.setAttribute('selected', true);
17959            
17960         }, this);
17961     }
17962
17963     /** 
17964     * @cfg {Boolean} grow 
17965     * @hide 
17966     */
17967     /** 
17968     * @cfg {Number} growMin 
17969     * @hide 
17970     */
17971     /** 
17972     * @cfg {Number} growMax 
17973     * @hide 
17974     */
17975     /**
17976      * @hide
17977      * @method autoSize
17978      */
17979 });
17980
17981 Roo.apply(Roo.bootstrap.ComboBox,  {
17982     
17983     header : {
17984         tag: 'div',
17985         cls: 'modal-header',
17986         cn: [
17987             {
17988                 tag: 'h4',
17989                 cls: 'modal-title'
17990             }
17991         ]
17992     },
17993     
17994     body : {
17995         tag: 'div',
17996         cls: 'modal-body',
17997         cn: [
17998             {
17999                 tag: 'ul',
18000                 cls: 'list-group'
18001             }
18002         ]
18003     },
18004     
18005     listItemRadio : {
18006         tag: 'li',
18007         cls: 'list-group-item',
18008         cn: [
18009             {
18010                 tag: 'span',
18011                 cls: 'roo-combobox-list-group-item-value'
18012             },
18013             {
18014                 tag: 'div',
18015                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18016                 cn: [
18017                     {
18018                         tag: 'input',
18019                         type: 'radio'
18020                     },
18021                     {
18022                         tag: 'label'
18023                     }
18024                 ]
18025             }
18026         ]
18027     },
18028     
18029     listItemCheckbox : {
18030         tag: 'li',
18031         cls: 'list-group-item',
18032         cn: [
18033             {
18034                 tag: 'span',
18035                 cls: 'roo-combobox-list-group-item-value'
18036             },
18037             {
18038                 tag: 'div',
18039                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18040                 cn: [
18041                     {
18042                         tag: 'input',
18043                         type: 'checkbox'
18044                     },
18045                     {
18046                         tag: 'label'
18047                     }
18048                 ]
18049             }
18050         ]
18051     },
18052     
18053     emptyResult : {
18054         tag: 'div',
18055         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18056     },
18057     
18058     footer : {
18059         tag: 'div',
18060         cls: 'modal-footer',
18061         cn: [
18062             {
18063                 tag: 'div',
18064                 cls: 'row',
18065                 cn: [
18066                     {
18067                         tag: 'div',
18068                         cls: 'col-xs-6 text-left',
18069                         cn: {
18070                             tag: 'button',
18071                             cls: 'btn btn-danger roo-touch-view-cancel',
18072                             html: 'Cancel'
18073                         }
18074                     },
18075                     {
18076                         tag: 'div',
18077                         cls: 'col-xs-6 text-right',
18078                         cn: {
18079                             tag: 'button',
18080                             cls: 'btn btn-success roo-touch-view-ok',
18081                             html: 'OK'
18082                         }
18083                     }
18084                 ]
18085             }
18086         ]
18087         
18088     }
18089 });
18090
18091 Roo.apply(Roo.bootstrap.ComboBox,  {
18092     
18093     touchViewTemplate : {
18094         tag: 'div',
18095         cls: 'modal fade roo-combobox-touch-view',
18096         cn: [
18097             {
18098                 tag: 'div',
18099                 cls: 'modal-dialog',
18100                 style : 'position:fixed', // we have to fix position....
18101                 cn: [
18102                     {
18103                         tag: 'div',
18104                         cls: 'modal-content',
18105                         cn: [
18106                             Roo.bootstrap.ComboBox.header,
18107                             Roo.bootstrap.ComboBox.body,
18108                             Roo.bootstrap.ComboBox.footer
18109                         ]
18110                     }
18111                 ]
18112             }
18113         ]
18114     }
18115 });/*
18116  * Based on:
18117  * Ext JS Library 1.1.1
18118  * Copyright(c) 2006-2007, Ext JS, LLC.
18119  *
18120  * Originally Released Under LGPL - original licence link has changed is not relivant.
18121  *
18122  * Fork - LGPL
18123  * <script type="text/javascript">
18124  */
18125
18126 /**
18127  * @class Roo.View
18128  * @extends Roo.util.Observable
18129  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18130  * This class also supports single and multi selection modes. <br>
18131  * Create a data model bound view:
18132  <pre><code>
18133  var store = new Roo.data.Store(...);
18134
18135  var view = new Roo.View({
18136     el : "my-element",
18137     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18138  
18139     singleSelect: true,
18140     selectedClass: "ydataview-selected",
18141     store: store
18142  });
18143
18144  // listen for node click?
18145  view.on("click", function(vw, index, node, e){
18146  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18147  });
18148
18149  // load XML data
18150  dataModel.load("foobar.xml");
18151  </code></pre>
18152  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18153  * <br><br>
18154  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18155  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18156  * 
18157  * Note: old style constructor is still suported (container, template, config)
18158  * 
18159  * @constructor
18160  * Create a new View
18161  * @param {Object} config The config object
18162  * 
18163  */
18164 Roo.View = function(config, depreciated_tpl, depreciated_config){
18165     
18166     this.parent = false;
18167     
18168     if (typeof(depreciated_tpl) == 'undefined') {
18169         // new way.. - universal constructor.
18170         Roo.apply(this, config);
18171         this.el  = Roo.get(this.el);
18172     } else {
18173         // old format..
18174         this.el  = Roo.get(config);
18175         this.tpl = depreciated_tpl;
18176         Roo.apply(this, depreciated_config);
18177     }
18178     this.wrapEl  = this.el.wrap().wrap();
18179     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18180     
18181     
18182     if(typeof(this.tpl) == "string"){
18183         this.tpl = new Roo.Template(this.tpl);
18184     } else {
18185         // support xtype ctors..
18186         this.tpl = new Roo.factory(this.tpl, Roo);
18187     }
18188     
18189     
18190     this.tpl.compile();
18191     
18192     /** @private */
18193     this.addEvents({
18194         /**
18195          * @event beforeclick
18196          * Fires before a click is processed. Returns false to cancel the default action.
18197          * @param {Roo.View} this
18198          * @param {Number} index The index of the target node
18199          * @param {HTMLElement} node The target node
18200          * @param {Roo.EventObject} e The raw event object
18201          */
18202             "beforeclick" : true,
18203         /**
18204          * @event click
18205          * Fires when a template node is clicked.
18206          * @param {Roo.View} this
18207          * @param {Number} index The index of the target node
18208          * @param {HTMLElement} node The target node
18209          * @param {Roo.EventObject} e The raw event object
18210          */
18211             "click" : true,
18212         /**
18213          * @event dblclick
18214          * Fires when a template node is double clicked.
18215          * @param {Roo.View} this
18216          * @param {Number} index The index of the target node
18217          * @param {HTMLElement} node The target node
18218          * @param {Roo.EventObject} e The raw event object
18219          */
18220             "dblclick" : true,
18221         /**
18222          * @event contextmenu
18223          * Fires when a template node is right clicked.
18224          * @param {Roo.View} this
18225          * @param {Number} index The index of the target node
18226          * @param {HTMLElement} node The target node
18227          * @param {Roo.EventObject} e The raw event object
18228          */
18229             "contextmenu" : true,
18230         /**
18231          * @event selectionchange
18232          * Fires when the selected nodes change.
18233          * @param {Roo.View} this
18234          * @param {Array} selections Array of the selected nodes
18235          */
18236             "selectionchange" : true,
18237     
18238         /**
18239          * @event beforeselect
18240          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18241          * @param {Roo.View} this
18242          * @param {HTMLElement} node The node to be selected
18243          * @param {Array} selections Array of currently selected nodes
18244          */
18245             "beforeselect" : true,
18246         /**
18247          * @event preparedata
18248          * Fires on every row to render, to allow you to change the data.
18249          * @param {Roo.View} this
18250          * @param {Object} data to be rendered (change this)
18251          */
18252           "preparedata" : true
18253           
18254           
18255         });
18256
18257
18258
18259     this.el.on({
18260         "click": this.onClick,
18261         "dblclick": this.onDblClick,
18262         "contextmenu": this.onContextMenu,
18263         scope:this
18264     });
18265
18266     this.selections = [];
18267     this.nodes = [];
18268     this.cmp = new Roo.CompositeElementLite([]);
18269     if(this.store){
18270         this.store = Roo.factory(this.store, Roo.data);
18271         this.setStore(this.store, true);
18272     }
18273     
18274     if ( this.footer && this.footer.xtype) {
18275            
18276          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18277         
18278         this.footer.dataSource = this.store;
18279         this.footer.container = fctr;
18280         this.footer = Roo.factory(this.footer, Roo);
18281         fctr.insertFirst(this.el);
18282         
18283         // this is a bit insane - as the paging toolbar seems to detach the el..
18284 //        dom.parentNode.parentNode.parentNode
18285          // they get detached?
18286     }
18287     
18288     
18289     Roo.View.superclass.constructor.call(this);
18290     
18291     
18292 };
18293
18294 Roo.extend(Roo.View, Roo.util.Observable, {
18295     
18296      /**
18297      * @cfg {Roo.data.Store} store Data store to load data from.
18298      */
18299     store : false,
18300     
18301     /**
18302      * @cfg {String|Roo.Element} el The container element.
18303      */
18304     el : '',
18305     
18306     /**
18307      * @cfg {String|Roo.Template} tpl The template used by this View 
18308      */
18309     tpl : false,
18310     /**
18311      * @cfg {String} dataName the named area of the template to use as the data area
18312      *                          Works with domtemplates roo-name="name"
18313      */
18314     dataName: false,
18315     /**
18316      * @cfg {String} selectedClass The css class to add to selected nodes
18317      */
18318     selectedClass : "x-view-selected",
18319      /**
18320      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18321      */
18322     emptyText : "",
18323     
18324     /**
18325      * @cfg {String} text to display on mask (default Loading)
18326      */
18327     mask : false,
18328     /**
18329      * @cfg {Boolean} multiSelect Allow multiple selection
18330      */
18331     multiSelect : false,
18332     /**
18333      * @cfg {Boolean} singleSelect Allow single selection
18334      */
18335     singleSelect:  false,
18336     
18337     /**
18338      * @cfg {Boolean} toggleSelect - selecting 
18339      */
18340     toggleSelect : false,
18341     
18342     /**
18343      * @cfg {Boolean} tickable - selecting 
18344      */
18345     tickable : false,
18346     
18347     /**
18348      * Returns the element this view is bound to.
18349      * @return {Roo.Element}
18350      */
18351     getEl : function(){
18352         return this.wrapEl;
18353     },
18354     
18355     
18356
18357     /**
18358      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18359      */
18360     refresh : function(){
18361         //Roo.log('refresh');
18362         var t = this.tpl;
18363         
18364         // if we are using something like 'domtemplate', then
18365         // the what gets used is:
18366         // t.applySubtemplate(NAME, data, wrapping data..)
18367         // the outer template then get' applied with
18368         //     the store 'extra data'
18369         // and the body get's added to the
18370         //      roo-name="data" node?
18371         //      <span class='roo-tpl-{name}'></span> ?????
18372         
18373         
18374         
18375         this.clearSelections();
18376         this.el.update("");
18377         var html = [];
18378         var records = this.store.getRange();
18379         if(records.length < 1) {
18380             
18381             // is this valid??  = should it render a template??
18382             
18383             this.el.update(this.emptyText);
18384             return;
18385         }
18386         var el = this.el;
18387         if (this.dataName) {
18388             this.el.update(t.apply(this.store.meta)); //????
18389             el = this.el.child('.roo-tpl-' + this.dataName);
18390         }
18391         
18392         for(var i = 0, len = records.length; i < len; i++){
18393             var data = this.prepareData(records[i].data, i, records[i]);
18394             this.fireEvent("preparedata", this, data, i, records[i]);
18395             
18396             var d = Roo.apply({}, data);
18397             
18398             if(this.tickable){
18399                 Roo.apply(d, {'roo-id' : Roo.id()});
18400                 
18401                 var _this = this;
18402             
18403                 Roo.each(this.parent.item, function(item){
18404                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18405                         return;
18406                     }
18407                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18408                 });
18409             }
18410             
18411             html[html.length] = Roo.util.Format.trim(
18412                 this.dataName ?
18413                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18414                     t.apply(d)
18415             );
18416         }
18417         
18418         
18419         
18420         el.update(html.join(""));
18421         this.nodes = el.dom.childNodes;
18422         this.updateIndexes(0);
18423     },
18424     
18425
18426     /**
18427      * Function to override to reformat the data that is sent to
18428      * the template for each node.
18429      * DEPRICATED - use the preparedata event handler.
18430      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18431      * a JSON object for an UpdateManager bound view).
18432      */
18433     prepareData : function(data, index, record)
18434     {
18435         this.fireEvent("preparedata", this, data, index, record);
18436         return data;
18437     },
18438
18439     onUpdate : function(ds, record){
18440         // Roo.log('on update');   
18441         this.clearSelections();
18442         var index = this.store.indexOf(record);
18443         var n = this.nodes[index];
18444         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18445         n.parentNode.removeChild(n);
18446         this.updateIndexes(index, index);
18447     },
18448
18449     
18450     
18451 // --------- FIXME     
18452     onAdd : function(ds, records, index)
18453     {
18454         //Roo.log(['on Add', ds, records, index] );        
18455         this.clearSelections();
18456         if(this.nodes.length == 0){
18457             this.refresh();
18458             return;
18459         }
18460         var n = this.nodes[index];
18461         for(var i = 0, len = records.length; i < len; i++){
18462             var d = this.prepareData(records[i].data, i, records[i]);
18463             if(n){
18464                 this.tpl.insertBefore(n, d);
18465             }else{
18466                 
18467                 this.tpl.append(this.el, d);
18468             }
18469         }
18470         this.updateIndexes(index);
18471     },
18472
18473     onRemove : function(ds, record, index){
18474        // Roo.log('onRemove');
18475         this.clearSelections();
18476         var el = this.dataName  ?
18477             this.el.child('.roo-tpl-' + this.dataName) :
18478             this.el; 
18479         
18480         el.dom.removeChild(this.nodes[index]);
18481         this.updateIndexes(index);
18482     },
18483
18484     /**
18485      * Refresh an individual node.
18486      * @param {Number} index
18487      */
18488     refreshNode : function(index){
18489         this.onUpdate(this.store, this.store.getAt(index));
18490     },
18491
18492     updateIndexes : function(startIndex, endIndex){
18493         var ns = this.nodes;
18494         startIndex = startIndex || 0;
18495         endIndex = endIndex || ns.length - 1;
18496         for(var i = startIndex; i <= endIndex; i++){
18497             ns[i].nodeIndex = i;
18498         }
18499     },
18500
18501     /**
18502      * Changes the data store this view uses and refresh the view.
18503      * @param {Store} store
18504      */
18505     setStore : function(store, initial){
18506         if(!initial && this.store){
18507             this.store.un("datachanged", this.refresh);
18508             this.store.un("add", this.onAdd);
18509             this.store.un("remove", this.onRemove);
18510             this.store.un("update", this.onUpdate);
18511             this.store.un("clear", this.refresh);
18512             this.store.un("beforeload", this.onBeforeLoad);
18513             this.store.un("load", this.onLoad);
18514             this.store.un("loadexception", this.onLoad);
18515         }
18516         if(store){
18517           
18518             store.on("datachanged", this.refresh, this);
18519             store.on("add", this.onAdd, this);
18520             store.on("remove", this.onRemove, this);
18521             store.on("update", this.onUpdate, this);
18522             store.on("clear", this.refresh, this);
18523             store.on("beforeload", this.onBeforeLoad, this);
18524             store.on("load", this.onLoad, this);
18525             store.on("loadexception", this.onLoad, this);
18526         }
18527         
18528         if(store){
18529             this.refresh();
18530         }
18531     },
18532     /**
18533      * onbeforeLoad - masks the loading area.
18534      *
18535      */
18536     onBeforeLoad : function(store,opts)
18537     {
18538          //Roo.log('onBeforeLoad');   
18539         if (!opts.add) {
18540             this.el.update("");
18541         }
18542         this.el.mask(this.mask ? this.mask : "Loading" ); 
18543     },
18544     onLoad : function ()
18545     {
18546         this.el.unmask();
18547     },
18548     
18549
18550     /**
18551      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18552      * @param {HTMLElement} node
18553      * @return {HTMLElement} The template node
18554      */
18555     findItemFromChild : function(node){
18556         var el = this.dataName  ?
18557             this.el.child('.roo-tpl-' + this.dataName,true) :
18558             this.el.dom; 
18559         
18560         if(!node || node.parentNode == el){
18561                     return node;
18562             }
18563             var p = node.parentNode;
18564             while(p && p != el){
18565             if(p.parentNode == el){
18566                 return p;
18567             }
18568             p = p.parentNode;
18569         }
18570             return null;
18571     },
18572
18573     /** @ignore */
18574     onClick : function(e){
18575         var item = this.findItemFromChild(e.getTarget());
18576         if(item){
18577             var index = this.indexOf(item);
18578             if(this.onItemClick(item, index, e) !== false){
18579                 this.fireEvent("click", this, index, item, e);
18580             }
18581         }else{
18582             this.clearSelections();
18583         }
18584     },
18585
18586     /** @ignore */
18587     onContextMenu : function(e){
18588         var item = this.findItemFromChild(e.getTarget());
18589         if(item){
18590             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18591         }
18592     },
18593
18594     /** @ignore */
18595     onDblClick : function(e){
18596         var item = this.findItemFromChild(e.getTarget());
18597         if(item){
18598             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18599         }
18600     },
18601
18602     onItemClick : function(item, index, e)
18603     {
18604         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18605             return false;
18606         }
18607         if (this.toggleSelect) {
18608             var m = this.isSelected(item) ? 'unselect' : 'select';
18609             //Roo.log(m);
18610             var _t = this;
18611             _t[m](item, true, false);
18612             return true;
18613         }
18614         if(this.multiSelect || this.singleSelect){
18615             if(this.multiSelect && e.shiftKey && this.lastSelection){
18616                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18617             }else{
18618                 this.select(item, this.multiSelect && e.ctrlKey);
18619                 this.lastSelection = item;
18620             }
18621             
18622             if(!this.tickable){
18623                 e.preventDefault();
18624             }
18625             
18626         }
18627         return true;
18628     },
18629
18630     /**
18631      * Get the number of selected nodes.
18632      * @return {Number}
18633      */
18634     getSelectionCount : function(){
18635         return this.selections.length;
18636     },
18637
18638     /**
18639      * Get the currently selected nodes.
18640      * @return {Array} An array of HTMLElements
18641      */
18642     getSelectedNodes : function(){
18643         return this.selections;
18644     },
18645
18646     /**
18647      * Get the indexes of the selected nodes.
18648      * @return {Array}
18649      */
18650     getSelectedIndexes : function(){
18651         var indexes = [], s = this.selections;
18652         for(var i = 0, len = s.length; i < len; i++){
18653             indexes.push(s[i].nodeIndex);
18654         }
18655         return indexes;
18656     },
18657
18658     /**
18659      * Clear all selections
18660      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18661      */
18662     clearSelections : function(suppressEvent){
18663         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18664             this.cmp.elements = this.selections;
18665             this.cmp.removeClass(this.selectedClass);
18666             this.selections = [];
18667             if(!suppressEvent){
18668                 this.fireEvent("selectionchange", this, this.selections);
18669             }
18670         }
18671     },
18672
18673     /**
18674      * Returns true if the passed node is selected
18675      * @param {HTMLElement/Number} node The node or node index
18676      * @return {Boolean}
18677      */
18678     isSelected : function(node){
18679         var s = this.selections;
18680         if(s.length < 1){
18681             return false;
18682         }
18683         node = this.getNode(node);
18684         return s.indexOf(node) !== -1;
18685     },
18686
18687     /**
18688      * Selects nodes.
18689      * @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
18690      * @param {Boolean} keepExisting (optional) true to keep existing selections
18691      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18692      */
18693     select : function(nodeInfo, keepExisting, suppressEvent){
18694         if(nodeInfo instanceof Array){
18695             if(!keepExisting){
18696                 this.clearSelections(true);
18697             }
18698             for(var i = 0, len = nodeInfo.length; i < len; i++){
18699                 this.select(nodeInfo[i], true, true);
18700             }
18701             return;
18702         } 
18703         var node = this.getNode(nodeInfo);
18704         if(!node || this.isSelected(node)){
18705             return; // already selected.
18706         }
18707         if(!keepExisting){
18708             this.clearSelections(true);
18709         }
18710         
18711         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18712             Roo.fly(node).addClass(this.selectedClass);
18713             this.selections.push(node);
18714             if(!suppressEvent){
18715                 this.fireEvent("selectionchange", this, this.selections);
18716             }
18717         }
18718         
18719         
18720     },
18721       /**
18722      * Unselects nodes.
18723      * @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
18724      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18725      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18726      */
18727     unselect : function(nodeInfo, keepExisting, suppressEvent)
18728     {
18729         if(nodeInfo instanceof Array){
18730             Roo.each(this.selections, function(s) {
18731                 this.unselect(s, nodeInfo);
18732             }, this);
18733             return;
18734         }
18735         var node = this.getNode(nodeInfo);
18736         if(!node || !this.isSelected(node)){
18737             //Roo.log("not selected");
18738             return; // not selected.
18739         }
18740         // fireevent???
18741         var ns = [];
18742         Roo.each(this.selections, function(s) {
18743             if (s == node ) {
18744                 Roo.fly(node).removeClass(this.selectedClass);
18745
18746                 return;
18747             }
18748             ns.push(s);
18749         },this);
18750         
18751         this.selections= ns;
18752         this.fireEvent("selectionchange", this, this.selections);
18753     },
18754
18755     /**
18756      * Gets a template node.
18757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18758      * @return {HTMLElement} The node or null if it wasn't found
18759      */
18760     getNode : function(nodeInfo){
18761         if(typeof nodeInfo == "string"){
18762             return document.getElementById(nodeInfo);
18763         }else if(typeof nodeInfo == "number"){
18764             return this.nodes[nodeInfo];
18765         }
18766         return nodeInfo;
18767     },
18768
18769     /**
18770      * Gets a range template nodes.
18771      * @param {Number} startIndex
18772      * @param {Number} endIndex
18773      * @return {Array} An array of nodes
18774      */
18775     getNodes : function(start, end){
18776         var ns = this.nodes;
18777         start = start || 0;
18778         end = typeof end == "undefined" ? ns.length - 1 : end;
18779         var nodes = [];
18780         if(start <= end){
18781             for(var i = start; i <= end; i++){
18782                 nodes.push(ns[i]);
18783             }
18784         } else{
18785             for(var i = start; i >= end; i--){
18786                 nodes.push(ns[i]);
18787             }
18788         }
18789         return nodes;
18790     },
18791
18792     /**
18793      * Finds the index of the passed node
18794      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18795      * @return {Number} The index of the node or -1
18796      */
18797     indexOf : function(node){
18798         node = this.getNode(node);
18799         if(typeof node.nodeIndex == "number"){
18800             return node.nodeIndex;
18801         }
18802         var ns = this.nodes;
18803         for(var i = 0, len = ns.length; i < len; i++){
18804             if(ns[i] == node){
18805                 return i;
18806             }
18807         }
18808         return -1;
18809     }
18810 });
18811 /*
18812  * - LGPL
18813  *
18814  * based on jquery fullcalendar
18815  * 
18816  */
18817
18818 Roo.bootstrap = Roo.bootstrap || {};
18819 /**
18820  * @class Roo.bootstrap.Calendar
18821  * @extends Roo.bootstrap.Component
18822  * Bootstrap Calendar class
18823  * @cfg {Boolean} loadMask (true|false) default false
18824  * @cfg {Object} header generate the user specific header of the calendar, default false
18825
18826  * @constructor
18827  * Create a new Container
18828  * @param {Object} config The config object
18829  */
18830
18831
18832
18833 Roo.bootstrap.Calendar = function(config){
18834     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18835      this.addEvents({
18836         /**
18837              * @event select
18838              * Fires when a date is selected
18839              * @param {DatePicker} this
18840              * @param {Date} date The selected date
18841              */
18842         'select': true,
18843         /**
18844              * @event monthchange
18845              * Fires when the displayed month changes 
18846              * @param {DatePicker} this
18847              * @param {Date} date The selected month
18848              */
18849         'monthchange': true,
18850         /**
18851              * @event evententer
18852              * Fires when mouse over an event
18853              * @param {Calendar} this
18854              * @param {event} Event
18855              */
18856         'evententer': true,
18857         /**
18858              * @event eventleave
18859              * Fires when the mouse leaves an
18860              * @param {Calendar} this
18861              * @param {event}
18862              */
18863         'eventleave': true,
18864         /**
18865              * @event eventclick
18866              * Fires when the mouse click an
18867              * @param {Calendar} this
18868              * @param {event}
18869              */
18870         'eventclick': true
18871         
18872     });
18873
18874 };
18875
18876 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18877     
18878      /**
18879      * @cfg {Number} startDay
18880      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18881      */
18882     startDay : 0,
18883     
18884     loadMask : false,
18885     
18886     header : false,
18887       
18888     getAutoCreate : function(){
18889         
18890         
18891         var fc_button = function(name, corner, style, content ) {
18892             return Roo.apply({},{
18893                 tag : 'span',
18894                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18895                          (corner.length ?
18896                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18897                             ''
18898                         ),
18899                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18900                 unselectable: 'on'
18901             });
18902         };
18903         
18904         var header = {};
18905         
18906         if(!this.header){
18907             header = {
18908                 tag : 'table',
18909                 cls : 'fc-header',
18910                 style : 'width:100%',
18911                 cn : [
18912                     {
18913                         tag: 'tr',
18914                         cn : [
18915                             {
18916                                 tag : 'td',
18917                                 cls : 'fc-header-left',
18918                                 cn : [
18919                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18920                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18921                                     { tag: 'span', cls: 'fc-header-space' },
18922                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18923
18924
18925                                 ]
18926                             },
18927
18928                             {
18929                                 tag : 'td',
18930                                 cls : 'fc-header-center',
18931                                 cn : [
18932                                     {
18933                                         tag: 'span',
18934                                         cls: 'fc-header-title',
18935                                         cn : {
18936                                             tag: 'H2',
18937                                             html : 'month / year'
18938                                         }
18939                                     }
18940
18941                                 ]
18942                             },
18943                             {
18944                                 tag : 'td',
18945                                 cls : 'fc-header-right',
18946                                 cn : [
18947                               /*      fc_button('month', 'left', '', 'month' ),
18948                                     fc_button('week', '', '', 'week' ),
18949                                     fc_button('day', 'right', '', 'day' )
18950                                 */    
18951
18952                                 ]
18953                             }
18954
18955                         ]
18956                     }
18957                 ]
18958             };
18959         }
18960         
18961         header = this.header;
18962         
18963        
18964         var cal_heads = function() {
18965             var ret = [];
18966             // fixme - handle this.
18967             
18968             for (var i =0; i < Date.dayNames.length; i++) {
18969                 var d = Date.dayNames[i];
18970                 ret.push({
18971                     tag: 'th',
18972                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18973                     html : d.substring(0,3)
18974                 });
18975                 
18976             }
18977             ret[0].cls += ' fc-first';
18978             ret[6].cls += ' fc-last';
18979             return ret;
18980         };
18981         var cal_cell = function(n) {
18982             return  {
18983                 tag: 'td',
18984                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18985                 cn : [
18986                     {
18987                         cn : [
18988                             {
18989                                 cls: 'fc-day-number',
18990                                 html: 'D'
18991                             },
18992                             {
18993                                 cls: 'fc-day-content',
18994                              
18995                                 cn : [
18996                                      {
18997                                         style: 'position: relative;' // height: 17px;
18998                                     }
18999                                 ]
19000                             }
19001                             
19002                             
19003                         ]
19004                     }
19005                 ]
19006                 
19007             }
19008         };
19009         var cal_rows = function() {
19010             
19011             var ret = [];
19012             for (var r = 0; r < 6; r++) {
19013                 var row= {
19014                     tag : 'tr',
19015                     cls : 'fc-week',
19016                     cn : []
19017                 };
19018                 
19019                 for (var i =0; i < Date.dayNames.length; i++) {
19020                     var d = Date.dayNames[i];
19021                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19022
19023                 }
19024                 row.cn[0].cls+=' fc-first';
19025                 row.cn[0].cn[0].style = 'min-height:90px';
19026                 row.cn[6].cls+=' fc-last';
19027                 ret.push(row);
19028                 
19029             }
19030             ret[0].cls += ' fc-first';
19031             ret[4].cls += ' fc-prev-last';
19032             ret[5].cls += ' fc-last';
19033             return ret;
19034             
19035         };
19036         
19037         var cal_table = {
19038             tag: 'table',
19039             cls: 'fc-border-separate',
19040             style : 'width:100%',
19041             cellspacing  : 0,
19042             cn : [
19043                 { 
19044                     tag: 'thead',
19045                     cn : [
19046                         { 
19047                             tag: 'tr',
19048                             cls : 'fc-first fc-last',
19049                             cn : cal_heads()
19050                         }
19051                     ]
19052                 },
19053                 { 
19054                     tag: 'tbody',
19055                     cn : cal_rows()
19056                 }
19057                   
19058             ]
19059         };
19060          
19061          var cfg = {
19062             cls : 'fc fc-ltr',
19063             cn : [
19064                 header,
19065                 {
19066                     cls : 'fc-content',
19067                     style : "position: relative;",
19068                     cn : [
19069                         {
19070                             cls : 'fc-view fc-view-month fc-grid',
19071                             style : 'position: relative',
19072                             unselectable : 'on',
19073                             cn : [
19074                                 {
19075                                     cls : 'fc-event-container',
19076                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19077                                 },
19078                                 cal_table
19079                             ]
19080                         }
19081                     ]
19082     
19083                 }
19084            ] 
19085             
19086         };
19087         
19088          
19089         
19090         return cfg;
19091     },
19092     
19093     
19094     initEvents : function()
19095     {
19096         if(!this.store){
19097             throw "can not find store for calendar";
19098         }
19099         
19100         var mark = {
19101             tag: "div",
19102             cls:"x-dlg-mask",
19103             style: "text-align:center",
19104             cn: [
19105                 {
19106                     tag: "div",
19107                     style: "background-color:white;width:50%;margin:250 auto",
19108                     cn: [
19109                         {
19110                             tag: "img",
19111                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19112                         },
19113                         {
19114                             tag: "span",
19115                             html: "Loading"
19116                         }
19117                         
19118                     ]
19119                 }
19120             ]
19121         };
19122         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19123         
19124         var size = this.el.select('.fc-content', true).first().getSize();
19125         this.maskEl.setSize(size.width, size.height);
19126         this.maskEl.enableDisplayMode("block");
19127         if(!this.loadMask){
19128             this.maskEl.hide();
19129         }
19130         
19131         this.store = Roo.factory(this.store, Roo.data);
19132         this.store.on('load', this.onLoad, this);
19133         this.store.on('beforeload', this.onBeforeLoad, this);
19134         
19135         this.resize();
19136         
19137         this.cells = this.el.select('.fc-day',true);
19138         //Roo.log(this.cells);
19139         this.textNodes = this.el.query('.fc-day-number');
19140         this.cells.addClassOnOver('fc-state-hover');
19141         
19142         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19143         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19144         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19145         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19146         
19147         this.on('monthchange', this.onMonthChange, this);
19148         
19149         this.update(new Date().clearTime());
19150     },
19151     
19152     resize : function() {
19153         var sz  = this.el.getSize();
19154         
19155         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19156         this.el.select('.fc-day-content div',true).setHeight(34);
19157     },
19158     
19159     
19160     // private
19161     showPrevMonth : function(e){
19162         this.update(this.activeDate.add("mo", -1));
19163     },
19164     showToday : function(e){
19165         this.update(new Date().clearTime());
19166     },
19167     // private
19168     showNextMonth : function(e){
19169         this.update(this.activeDate.add("mo", 1));
19170     },
19171
19172     // private
19173     showPrevYear : function(){
19174         this.update(this.activeDate.add("y", -1));
19175     },
19176
19177     // private
19178     showNextYear : function(){
19179         this.update(this.activeDate.add("y", 1));
19180     },
19181
19182     
19183    // private
19184     update : function(date)
19185     {
19186         var vd = this.activeDate;
19187         this.activeDate = date;
19188 //        if(vd && this.el){
19189 //            var t = date.getTime();
19190 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19191 //                Roo.log('using add remove');
19192 //                
19193 //                this.fireEvent('monthchange', this, date);
19194 //                
19195 //                this.cells.removeClass("fc-state-highlight");
19196 //                this.cells.each(function(c){
19197 //                   if(c.dateValue == t){
19198 //                       c.addClass("fc-state-highlight");
19199 //                       setTimeout(function(){
19200 //                            try{c.dom.firstChild.focus();}catch(e){}
19201 //                       }, 50);
19202 //                       return false;
19203 //                   }
19204 //                   return true;
19205 //                });
19206 //                return;
19207 //            }
19208 //        }
19209         
19210         var days = date.getDaysInMonth();
19211         
19212         var firstOfMonth = date.getFirstDateOfMonth();
19213         var startingPos = firstOfMonth.getDay()-this.startDay;
19214         
19215         if(startingPos < this.startDay){
19216             startingPos += 7;
19217         }
19218         
19219         var pm = date.add(Date.MONTH, -1);
19220         var prevStart = pm.getDaysInMonth()-startingPos;
19221 //        
19222         this.cells = this.el.select('.fc-day',true);
19223         this.textNodes = this.el.query('.fc-day-number');
19224         this.cells.addClassOnOver('fc-state-hover');
19225         
19226         var cells = this.cells.elements;
19227         var textEls = this.textNodes;
19228         
19229         Roo.each(cells, function(cell){
19230             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19231         });
19232         
19233         days += startingPos;
19234
19235         // convert everything to numbers so it's fast
19236         var day = 86400000;
19237         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19238         //Roo.log(d);
19239         //Roo.log(pm);
19240         //Roo.log(prevStart);
19241         
19242         var today = new Date().clearTime().getTime();
19243         var sel = date.clearTime().getTime();
19244         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19245         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19246         var ddMatch = this.disabledDatesRE;
19247         var ddText = this.disabledDatesText;
19248         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19249         var ddaysText = this.disabledDaysText;
19250         var format = this.format;
19251         
19252         var setCellClass = function(cal, cell){
19253             cell.row = 0;
19254             cell.events = [];
19255             cell.more = [];
19256             //Roo.log('set Cell Class');
19257             cell.title = "";
19258             var t = d.getTime();
19259             
19260             //Roo.log(d);
19261             
19262             cell.dateValue = t;
19263             if(t == today){
19264                 cell.className += " fc-today";
19265                 cell.className += " fc-state-highlight";
19266                 cell.title = cal.todayText;
19267             }
19268             if(t == sel){
19269                 // disable highlight in other month..
19270                 //cell.className += " fc-state-highlight";
19271                 
19272             }
19273             // disabling
19274             if(t < min) {
19275                 cell.className = " fc-state-disabled";
19276                 cell.title = cal.minText;
19277                 return;
19278             }
19279             if(t > max) {
19280                 cell.className = " fc-state-disabled";
19281                 cell.title = cal.maxText;
19282                 return;
19283             }
19284             if(ddays){
19285                 if(ddays.indexOf(d.getDay()) != -1){
19286                     cell.title = ddaysText;
19287                     cell.className = " fc-state-disabled";
19288                 }
19289             }
19290             if(ddMatch && format){
19291                 var fvalue = d.dateFormat(format);
19292                 if(ddMatch.test(fvalue)){
19293                     cell.title = ddText.replace("%0", fvalue);
19294                     cell.className = " fc-state-disabled";
19295                 }
19296             }
19297             
19298             if (!cell.initialClassName) {
19299                 cell.initialClassName = cell.dom.className;
19300             }
19301             
19302             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19303         };
19304
19305         var i = 0;
19306         
19307         for(; i < startingPos; i++) {
19308             textEls[i].innerHTML = (++prevStart);
19309             d.setDate(d.getDate()+1);
19310             
19311             cells[i].className = "fc-past fc-other-month";
19312             setCellClass(this, cells[i]);
19313         }
19314         
19315         var intDay = 0;
19316         
19317         for(; i < days; i++){
19318             intDay = i - startingPos + 1;
19319             textEls[i].innerHTML = (intDay);
19320             d.setDate(d.getDate()+1);
19321             
19322             cells[i].className = ''; // "x-date-active";
19323             setCellClass(this, cells[i]);
19324         }
19325         var extraDays = 0;
19326         
19327         for(; i < 42; i++) {
19328             textEls[i].innerHTML = (++extraDays);
19329             d.setDate(d.getDate()+1);
19330             
19331             cells[i].className = "fc-future fc-other-month";
19332             setCellClass(this, cells[i]);
19333         }
19334         
19335         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19336         
19337         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19338         
19339         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19340         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19341         
19342         if(totalRows != 6){
19343             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19344             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19345         }
19346         
19347         this.fireEvent('monthchange', this, date);
19348         
19349         
19350         /*
19351         if(!this.internalRender){
19352             var main = this.el.dom.firstChild;
19353             var w = main.offsetWidth;
19354             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19355             Roo.fly(main).setWidth(w);
19356             this.internalRender = true;
19357             // opera does not respect the auto grow header center column
19358             // then, after it gets a width opera refuses to recalculate
19359             // without a second pass
19360             if(Roo.isOpera && !this.secondPass){
19361                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19362                 this.secondPass = true;
19363                 this.update.defer(10, this, [date]);
19364             }
19365         }
19366         */
19367         
19368     },
19369     
19370     findCell : function(dt) {
19371         dt = dt.clearTime().getTime();
19372         var ret = false;
19373         this.cells.each(function(c){
19374             //Roo.log("check " +c.dateValue + '?=' + dt);
19375             if(c.dateValue == dt){
19376                 ret = c;
19377                 return false;
19378             }
19379             return true;
19380         });
19381         
19382         return ret;
19383     },
19384     
19385     findCells : function(ev) {
19386         var s = ev.start.clone().clearTime().getTime();
19387        // Roo.log(s);
19388         var e= ev.end.clone().clearTime().getTime();
19389        // Roo.log(e);
19390         var ret = [];
19391         this.cells.each(function(c){
19392              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19393             
19394             if(c.dateValue > e){
19395                 return ;
19396             }
19397             if(c.dateValue < s){
19398                 return ;
19399             }
19400             ret.push(c);
19401         });
19402         
19403         return ret;    
19404     },
19405     
19406 //    findBestRow: function(cells)
19407 //    {
19408 //        var ret = 0;
19409 //        
19410 //        for (var i =0 ; i < cells.length;i++) {
19411 //            ret  = Math.max(cells[i].rows || 0,ret);
19412 //        }
19413 //        return ret;
19414 //        
19415 //    },
19416     
19417     
19418     addItem : function(ev)
19419     {
19420         // look for vertical location slot in
19421         var cells = this.findCells(ev);
19422         
19423 //        ev.row = this.findBestRow(cells);
19424         
19425         // work out the location.
19426         
19427         var crow = false;
19428         var rows = [];
19429         for(var i =0; i < cells.length; i++) {
19430             
19431             cells[i].row = cells[0].row;
19432             
19433             if(i == 0){
19434                 cells[i].row = cells[i].row + 1;
19435             }
19436             
19437             if (!crow) {
19438                 crow = {
19439                     start : cells[i],
19440                     end :  cells[i]
19441                 };
19442                 continue;
19443             }
19444             if (crow.start.getY() == cells[i].getY()) {
19445                 // on same row.
19446                 crow.end = cells[i];
19447                 continue;
19448             }
19449             // different row.
19450             rows.push(crow);
19451             crow = {
19452                 start: cells[i],
19453                 end : cells[i]
19454             };
19455             
19456         }
19457         
19458         rows.push(crow);
19459         ev.els = [];
19460         ev.rows = rows;
19461         ev.cells = cells;
19462         
19463         cells[0].events.push(ev);
19464         
19465         this.calevents.push(ev);
19466     },
19467     
19468     clearEvents: function() {
19469         
19470         if(!this.calevents){
19471             return;
19472         }
19473         
19474         Roo.each(this.cells.elements, function(c){
19475             c.row = 0;
19476             c.events = [];
19477             c.more = [];
19478         });
19479         
19480         Roo.each(this.calevents, function(e) {
19481             Roo.each(e.els, function(el) {
19482                 el.un('mouseenter' ,this.onEventEnter, this);
19483                 el.un('mouseleave' ,this.onEventLeave, this);
19484                 el.remove();
19485             },this);
19486         },this);
19487         
19488         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19489             e.remove();
19490         });
19491         
19492     },
19493     
19494     renderEvents: function()
19495     {   
19496         var _this = this;
19497         
19498         this.cells.each(function(c) {
19499             
19500             if(c.row < 5){
19501                 return;
19502             }
19503             
19504             var ev = c.events;
19505             
19506             var r = 4;
19507             if(c.row != c.events.length){
19508                 r = 4 - (4 - (c.row - c.events.length));
19509             }
19510             
19511             c.events = ev.slice(0, r);
19512             c.more = ev.slice(r);
19513             
19514             if(c.more.length && c.more.length == 1){
19515                 c.events.push(c.more.pop());
19516             }
19517             
19518             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19519             
19520         });
19521             
19522         this.cells.each(function(c) {
19523             
19524             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19525             
19526             
19527             for (var e = 0; e < c.events.length; e++){
19528                 var ev = c.events[e];
19529                 var rows = ev.rows;
19530                 
19531                 for(var i = 0; i < rows.length; i++) {
19532                 
19533                     // how many rows should it span..
19534
19535                     var  cfg = {
19536                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19537                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19538
19539                         unselectable : "on",
19540                         cn : [
19541                             {
19542                                 cls: 'fc-event-inner',
19543                                 cn : [
19544     //                                {
19545     //                                  tag:'span',
19546     //                                  cls: 'fc-event-time',
19547     //                                  html : cells.length > 1 ? '' : ev.time
19548     //                                },
19549                                     {
19550                                       tag:'span',
19551                                       cls: 'fc-event-title',
19552                                       html : String.format('{0}', ev.title)
19553                                     }
19554
19555
19556                                 ]
19557                             },
19558                             {
19559                                 cls: 'ui-resizable-handle ui-resizable-e',
19560                                 html : '&nbsp;&nbsp;&nbsp'
19561                             }
19562
19563                         ]
19564                     };
19565
19566                     if (i == 0) {
19567                         cfg.cls += ' fc-event-start';
19568                     }
19569                     if ((i+1) == rows.length) {
19570                         cfg.cls += ' fc-event-end';
19571                     }
19572
19573                     var ctr = _this.el.select('.fc-event-container',true).first();
19574                     var cg = ctr.createChild(cfg);
19575
19576                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19577                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19578
19579                     var r = (c.more.length) ? 1 : 0;
19580                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19581                     cg.setWidth(ebox.right - sbox.x -2);
19582
19583                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19584                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19585                     cg.on('click', _this.onEventClick, _this, ev);
19586
19587                     ev.els.push(cg);
19588                     
19589                 }
19590                 
19591             }
19592             
19593             
19594             if(c.more.length){
19595                 var  cfg = {
19596                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19597                     style : 'position: absolute',
19598                     unselectable : "on",
19599                     cn : [
19600                         {
19601                             cls: 'fc-event-inner',
19602                             cn : [
19603                                 {
19604                                   tag:'span',
19605                                   cls: 'fc-event-title',
19606                                   html : 'More'
19607                                 }
19608
19609
19610                             ]
19611                         },
19612                         {
19613                             cls: 'ui-resizable-handle ui-resizable-e',
19614                             html : '&nbsp;&nbsp;&nbsp'
19615                         }
19616
19617                     ]
19618                 };
19619
19620                 var ctr = _this.el.select('.fc-event-container',true).first();
19621                 var cg = ctr.createChild(cfg);
19622
19623                 var sbox = c.select('.fc-day-content',true).first().getBox();
19624                 var ebox = c.select('.fc-day-content',true).first().getBox();
19625                 //Roo.log(cg);
19626                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19627                 cg.setWidth(ebox.right - sbox.x -2);
19628
19629                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19630                 
19631             }
19632             
19633         });
19634         
19635         
19636         
19637     },
19638     
19639     onEventEnter: function (e, el,event,d) {
19640         this.fireEvent('evententer', this, el, event);
19641     },
19642     
19643     onEventLeave: function (e, el,event,d) {
19644         this.fireEvent('eventleave', this, el, event);
19645     },
19646     
19647     onEventClick: function (e, el,event,d) {
19648         this.fireEvent('eventclick', this, el, event);
19649     },
19650     
19651     onMonthChange: function () {
19652         this.store.load();
19653     },
19654     
19655     onMoreEventClick: function(e, el, more)
19656     {
19657         var _this = this;
19658         
19659         this.calpopover.placement = 'right';
19660         this.calpopover.setTitle('More');
19661         
19662         this.calpopover.setContent('');
19663         
19664         var ctr = this.calpopover.el.select('.popover-content', true).first();
19665         
19666         Roo.each(more, function(m){
19667             var cfg = {
19668                 cls : 'fc-event-hori fc-event-draggable',
19669                 html : m.title
19670             };
19671             var cg = ctr.createChild(cfg);
19672             
19673             cg.on('click', _this.onEventClick, _this, m);
19674         });
19675         
19676         this.calpopover.show(el);
19677         
19678         
19679     },
19680     
19681     onLoad: function () 
19682     {   
19683         this.calevents = [];
19684         var cal = this;
19685         
19686         if(this.store.getCount() > 0){
19687             this.store.data.each(function(d){
19688                cal.addItem({
19689                     id : d.data.id,
19690                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19691                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19692                     time : d.data.start_time,
19693                     title : d.data.title,
19694                     description : d.data.description,
19695                     venue : d.data.venue
19696                 });
19697             });
19698         }
19699         
19700         this.renderEvents();
19701         
19702         if(this.calevents.length && this.loadMask){
19703             this.maskEl.hide();
19704         }
19705     },
19706     
19707     onBeforeLoad: function()
19708     {
19709         this.clearEvents();
19710         if(this.loadMask){
19711             this.maskEl.show();
19712         }
19713     }
19714 });
19715
19716  
19717  /*
19718  * - LGPL
19719  *
19720  * element
19721  * 
19722  */
19723
19724 /**
19725  * @class Roo.bootstrap.Popover
19726  * @extends Roo.bootstrap.Component
19727  * Bootstrap Popover class
19728  * @cfg {String} html contents of the popover   (or false to use children..)
19729  * @cfg {String} title of popover (or false to hide)
19730  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19731  * @cfg {String} trigger click || hover (or false to trigger manually)
19732  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19733  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19734  *      - if false and it has a 'parent' then it will be automatically added to that element
19735  *      - if string - Roo.get  will be called 
19736  * @cfg {Number} delay - delay before showing
19737  
19738  * @constructor
19739  * Create a new Popover
19740  * @param {Object} config The config object
19741  */
19742
19743 Roo.bootstrap.Popover = function(config){
19744     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19745     
19746     this.addEvents({
19747         // raw events
19748          /**
19749          * @event show
19750          * After the popover show
19751          * 
19752          * @param {Roo.bootstrap.Popover} this
19753          */
19754         "show" : true,
19755         /**
19756          * @event hide
19757          * After the popover hide
19758          * 
19759          * @param {Roo.bootstrap.Popover} this
19760          */
19761         "hide" : true
19762     });
19763 };
19764
19765 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19766     
19767     title: false,
19768     html: false,
19769     
19770     placement : 'right',
19771     trigger : 'hover', // hover
19772     modal : false,
19773     delay : 0,
19774     
19775     over: false,
19776     
19777     can_build_overlaid : false,
19778     
19779     maskEl : false, // the mask element
19780     headerEl : false,
19781     contentEl : false,
19782     alignEl : false, // when show is called with an element - this get's stored.
19783     
19784     getChildContainer : function()
19785     {
19786         return this.contentEl;
19787         
19788     },
19789     getPopoverHeader : function()
19790     {
19791         this.title = true; // flag not to hide it..
19792         this.headerEl.addClass('p-0');
19793         return this.headerEl
19794     },
19795     
19796     
19797     getAutoCreate : function(){
19798          
19799         var cfg = {
19800            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19801            style: 'display:block',
19802            cn : [
19803                 {
19804                     cls : 'arrow'
19805                 },
19806                 {
19807                     cls : 'popover-inner ',
19808                     cn : [
19809                         {
19810                             tag: 'h3',
19811                             cls: 'popover-title popover-header',
19812                             html : this.title === false ? '' : this.title
19813                         },
19814                         {
19815                             cls : 'popover-content popover-body '  + (this.cls || ''),
19816                             html : this.html || ''
19817                         }
19818                     ]
19819                     
19820                 }
19821            ]
19822         };
19823         
19824         return cfg;
19825     },
19826     /**
19827      * @param {string} the title
19828      */
19829     setTitle: function(str)
19830     {
19831         this.title = str;
19832         if (this.el) {
19833             this.headerEl.dom.innerHTML = str;
19834         }
19835         
19836     },
19837     /**
19838      * @param {string} the body content
19839      */
19840     setContent: function(str)
19841     {
19842         this.html = str;
19843         if (this.contentEl) {
19844             this.contentEl.dom.innerHTML = str;
19845         }
19846         
19847     },
19848     // as it get's added to the bottom of the page.
19849     onRender : function(ct, position)
19850     {
19851         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19852         
19853         
19854         
19855         if(!this.el){
19856             var cfg = Roo.apply({},  this.getAutoCreate());
19857             cfg.id = Roo.id();
19858             
19859             if (this.cls) {
19860                 cfg.cls += ' ' + this.cls;
19861             }
19862             if (this.style) {
19863                 cfg.style = this.style;
19864             }
19865             //Roo.log("adding to ");
19866             this.el = Roo.get(document.body).createChild(cfg, position);
19867 //            Roo.log(this.el);
19868         }
19869         
19870         this.contentEl = this.el.select('.popover-content',true).first();
19871         this.headerEl =  this.el.select('.popover-title',true).first();
19872         
19873         var nitems = [];
19874         if(typeof(this.items) != 'undefined'){
19875             var items = this.items;
19876             delete this.items;
19877
19878             for(var i =0;i < items.length;i++) {
19879                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19880             }
19881         }
19882
19883         this.items = nitems;
19884         
19885         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19886         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19887         
19888         
19889         
19890         this.initEvents();
19891     },
19892     
19893     resizeMask : function()
19894     {
19895         this.maskEl.setSize(
19896             Roo.lib.Dom.getViewWidth(true),
19897             Roo.lib.Dom.getViewHeight(true)
19898         );
19899     },
19900     
19901     initEvents : function()
19902     {
19903         
19904         if (!this.modal) { 
19905             Roo.bootstrap.Popover.register(this);
19906         }
19907          
19908         this.arrowEl = this.el.select('.arrow',true).first();
19909         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19910         this.el.enableDisplayMode('block');
19911         this.el.hide();
19912  
19913         
19914         if (this.over === false && !this.parent()) {
19915             return; 
19916         }
19917         if (this.triggers === false) {
19918             return;
19919         }
19920          
19921         // support parent
19922         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19923         var triggers = this.trigger ? this.trigger.split(' ') : [];
19924         Roo.each(triggers, function(trigger) {
19925         
19926             if (trigger == 'click') {
19927                 on_el.on('click', this.toggle, this);
19928             } else if (trigger != 'manual') {
19929                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19930                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19931       
19932                 on_el.on(eventIn  ,this.enter, this);
19933                 on_el.on(eventOut, this.leave, this);
19934             }
19935         }, this);
19936     },
19937     
19938     
19939     // private
19940     timeout : null,
19941     hoverState : null,
19942     
19943     toggle : function () {
19944         this.hoverState == 'in' ? this.leave() : this.enter();
19945     },
19946     
19947     enter : function () {
19948         
19949         clearTimeout(this.timeout);
19950     
19951         this.hoverState = 'in';
19952     
19953         if (!this.delay || !this.delay.show) {
19954             this.show();
19955             return;
19956         }
19957         var _t = this;
19958         this.timeout = setTimeout(function () {
19959             if (_t.hoverState == 'in') {
19960                 _t.show();
19961             }
19962         }, this.delay.show)
19963     },
19964     
19965     leave : function() {
19966         clearTimeout(this.timeout);
19967     
19968         this.hoverState = 'out';
19969     
19970         if (!this.delay || !this.delay.hide) {
19971             this.hide();
19972             return;
19973         }
19974         var _t = this;
19975         this.timeout = setTimeout(function () {
19976             if (_t.hoverState == 'out') {
19977                 _t.hide();
19978             }
19979         }, this.delay.hide)
19980     },
19981     /**
19982      * Show the popover
19983      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19984      * @param {string} (left|right|top|bottom) position
19985      */
19986     show : function (on_el, placement)
19987     {
19988         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19989         on_el = on_el || false; // default to false
19990          
19991         if (!on_el) {
19992             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19993                 on_el = this.parent().el;
19994             } else if (this.over) {
19995                 Roo.get(this.over);
19996             }
19997             
19998         }
19999         
20000         this.alignEl = Roo.get( on_el );
20001
20002         if (!this.el) {
20003             this.render(document.body);
20004         }
20005         
20006         
20007          
20008         
20009         if (this.title === false) {
20010             this.headerEl.hide();
20011         }
20012         
20013        
20014         this.el.show();
20015         this.el.dom.style.display = 'block';
20016          
20017  
20018         if (this.alignEl) {
20019             this.updatePosition(this.placement, true);
20020              
20021         } else {
20022             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20023             var es = this.el.getSize();
20024             var x = Roo.lib.Dom.getViewWidth()/2;
20025             var y = Roo.lib.Dom.getViewHeight()/2;
20026             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20027             
20028         }
20029
20030         
20031         //var arrow = this.el.select('.arrow',true).first();
20032         //arrow.set(align[2], 
20033         
20034         this.el.addClass('in');
20035         
20036          
20037         
20038         this.hoverState = 'in';
20039         
20040         if (this.modal) {
20041             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20042             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20043             this.maskEl.dom.style.display = 'block';
20044             this.maskEl.addClass('show');
20045         }
20046         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20047  
20048         this.fireEvent('show', this);
20049         
20050     },
20051     /**
20052      * fire this manually after loading a grid in the table for example
20053      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20054      * @param {Boolean} try and move it if we cant get right position.
20055      */
20056     updatePosition : function(placement, try_move)
20057     {
20058         // allow for calling with no parameters
20059         placement = placement   ? placement :  this.placement;
20060         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20061         
20062         this.el.removeClass([
20063             'fade','top','bottom', 'left', 'right','in',
20064             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20065         ]);
20066         this.el.addClass(placement + ' bs-popover-' + placement);
20067         
20068         if (!this.alignEl ) {
20069             return false;
20070         }
20071         
20072         switch (placement) {
20073             case 'right':
20074                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20075                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20076                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20077                     //normal display... or moved up/down.
20078                     this.el.setXY(offset);
20079                     var xy = this.alignEl.getAnchorXY('tr', false);
20080                     xy[0]+=2;xy[1]+=5;
20081                     this.arrowEl.setXY(xy);
20082                     return true;
20083                 }
20084                 // continue through...
20085                 return this.updatePosition('left', false);
20086                 
20087             
20088             case 'left':
20089                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20090                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20091                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20092                     //normal display... or moved up/down.
20093                     this.el.setXY(offset);
20094                     var xy = this.alignEl.getAnchorXY('tl', false);
20095                     xy[0]-=10;xy[1]+=5; // << fix me
20096                     this.arrowEl.setXY(xy);
20097                     return true;
20098                 }
20099                 // call self...
20100                 return this.updatePosition('right', false);
20101             
20102             case 'top':
20103                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20104                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20105                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20106                     //normal display... or moved up/down.
20107                     this.el.setXY(offset);
20108                     var xy = this.alignEl.getAnchorXY('t', false);
20109                     xy[1]-=10; // << fix me
20110                     this.arrowEl.setXY(xy);
20111                     return true;
20112                 }
20113                 // fall through
20114                return this.updatePosition('bottom', false);
20115             
20116             case 'bottom':
20117                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20118                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20119                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20120                     //normal display... or moved up/down.
20121                     this.el.setXY(offset);
20122                     var xy = this.alignEl.getAnchorXY('b', false);
20123                      xy[1]+=2; // << fix me
20124                     this.arrowEl.setXY(xy);
20125                     return true;
20126                 }
20127                 // fall through
20128                 return this.updatePosition('top', false);
20129                 
20130             
20131         }
20132         
20133         
20134         return false;
20135     },
20136     
20137     hide : function()
20138     {
20139         this.el.setXY([0,0]);
20140         this.el.removeClass('in');
20141         this.el.hide();
20142         this.hoverState = null;
20143         this.maskEl.hide(); // always..
20144         this.fireEvent('hide', this);
20145     }
20146     
20147 });
20148
20149
20150 Roo.apply(Roo.bootstrap.Popover, {
20151
20152     alignment : {
20153         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20154         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20155         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20156         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20157     },
20158     
20159     zIndex : 20001,
20160
20161     clickHander : false,
20162     
20163
20164     onMouseDown : function(e)
20165     {
20166         if (!e.getTarget(".roo-popover")) {
20167             this.hideAll();
20168         }
20169          
20170     },
20171     
20172     popups : [],
20173     
20174     register : function(popup)
20175     {
20176         if (!Roo.bootstrap.Popover.clickHandler) {
20177             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20178         }
20179         // hide other popups.
20180         this.hideAll();
20181         this.popups.push(popup);
20182     },
20183     hideAll : function()
20184     {
20185         this.popups.forEach(function(p) {
20186             p.hide();
20187         });
20188     }
20189
20190 });/*
20191  * - LGPL
20192  *
20193  * Card header - holder for the card header elements.
20194  * 
20195  */
20196
20197 /**
20198  * @class Roo.bootstrap.PopoverNav
20199  * @extends Roo.bootstrap.NavGroup
20200  * Bootstrap Popover header navigation class
20201  * @constructor
20202  * Create a new Popover Header Navigation 
20203  * @param {Object} config The config object
20204  */
20205
20206 Roo.bootstrap.PopoverNav = function(config){
20207     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20208 };
20209
20210 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20211     
20212     
20213     container_method : 'getPopoverHeader' 
20214     
20215      
20216     
20217     
20218    
20219 });
20220
20221  
20222
20223  /*
20224  * - LGPL
20225  *
20226  * Progress
20227  * 
20228  */
20229
20230 /**
20231  * @class Roo.bootstrap.Progress
20232  * @extends Roo.bootstrap.Component
20233  * Bootstrap Progress class
20234  * @cfg {Boolean} striped striped of the progress bar
20235  * @cfg {Boolean} active animated of the progress bar
20236  * 
20237  * 
20238  * @constructor
20239  * Create a new Progress
20240  * @param {Object} config The config object
20241  */
20242
20243 Roo.bootstrap.Progress = function(config){
20244     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20245 };
20246
20247 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20248     
20249     striped : false,
20250     active: false,
20251     
20252     getAutoCreate : function(){
20253         var cfg = {
20254             tag: 'div',
20255             cls: 'progress'
20256         };
20257         
20258         
20259         if(this.striped){
20260             cfg.cls += ' progress-striped';
20261         }
20262       
20263         if(this.active){
20264             cfg.cls += ' active';
20265         }
20266         
20267         
20268         return cfg;
20269     }
20270    
20271 });
20272
20273  
20274
20275  /*
20276  * - LGPL
20277  *
20278  * ProgressBar
20279  * 
20280  */
20281
20282 /**
20283  * @class Roo.bootstrap.ProgressBar
20284  * @extends Roo.bootstrap.Component
20285  * Bootstrap ProgressBar class
20286  * @cfg {Number} aria_valuenow aria-value now
20287  * @cfg {Number} aria_valuemin aria-value min
20288  * @cfg {Number} aria_valuemax aria-value max
20289  * @cfg {String} label label for the progress bar
20290  * @cfg {String} panel (success | info | warning | danger )
20291  * @cfg {String} role role of the progress bar
20292  * @cfg {String} sr_only text
20293  * 
20294  * 
20295  * @constructor
20296  * Create a new ProgressBar
20297  * @param {Object} config The config object
20298  */
20299
20300 Roo.bootstrap.ProgressBar = function(config){
20301     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20302 };
20303
20304 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20305     
20306     aria_valuenow : 0,
20307     aria_valuemin : 0,
20308     aria_valuemax : 100,
20309     label : false,
20310     panel : false,
20311     role : false,
20312     sr_only: false,
20313     
20314     getAutoCreate : function()
20315     {
20316         
20317         var cfg = {
20318             tag: 'div',
20319             cls: 'progress-bar',
20320             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20321         };
20322         
20323         if(this.sr_only){
20324             cfg.cn = {
20325                 tag: 'span',
20326                 cls: 'sr-only',
20327                 html: this.sr_only
20328             }
20329         }
20330         
20331         if(this.role){
20332             cfg.role = this.role;
20333         }
20334         
20335         if(this.aria_valuenow){
20336             cfg['aria-valuenow'] = this.aria_valuenow;
20337         }
20338         
20339         if(this.aria_valuemin){
20340             cfg['aria-valuemin'] = this.aria_valuemin;
20341         }
20342         
20343         if(this.aria_valuemax){
20344             cfg['aria-valuemax'] = this.aria_valuemax;
20345         }
20346         
20347         if(this.label && !this.sr_only){
20348             cfg.html = this.label;
20349         }
20350         
20351         if(this.panel){
20352             cfg.cls += ' progress-bar-' + this.panel;
20353         }
20354         
20355         return cfg;
20356     },
20357     
20358     update : function(aria_valuenow)
20359     {
20360         this.aria_valuenow = aria_valuenow;
20361         
20362         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20363     }
20364    
20365 });
20366
20367  
20368
20369  /*
20370  * - LGPL
20371  *
20372  * column
20373  * 
20374  */
20375
20376 /**
20377  * @class Roo.bootstrap.TabGroup
20378  * @extends Roo.bootstrap.Column
20379  * Bootstrap Column class
20380  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20381  * @cfg {Boolean} carousel true to make the group behave like a carousel
20382  * @cfg {Boolean} bullets show bullets for the panels
20383  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20384  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20385  * @cfg {Boolean} showarrow (true|false) show arrow default true
20386  * 
20387  * @constructor
20388  * Create a new TabGroup
20389  * @param {Object} config The config object
20390  */
20391
20392 Roo.bootstrap.TabGroup = function(config){
20393     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20394     if (!this.navId) {
20395         this.navId = Roo.id();
20396     }
20397     this.tabs = [];
20398     Roo.bootstrap.TabGroup.register(this);
20399     
20400 };
20401
20402 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20403     
20404     carousel : false,
20405     transition : false,
20406     bullets : 0,
20407     timer : 0,
20408     autoslide : false,
20409     slideFn : false,
20410     slideOnTouch : false,
20411     showarrow : true,
20412     
20413     getAutoCreate : function()
20414     {
20415         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20416         
20417         cfg.cls += ' tab-content';
20418         
20419         if (this.carousel) {
20420             cfg.cls += ' carousel slide';
20421             
20422             cfg.cn = [{
20423                cls : 'carousel-inner',
20424                cn : []
20425             }];
20426         
20427             if(this.bullets  && !Roo.isTouch){
20428                 
20429                 var bullets = {
20430                     cls : 'carousel-bullets',
20431                     cn : []
20432                 };
20433                
20434                 if(this.bullets_cls){
20435                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20436                 }
20437                 
20438                 bullets.cn.push({
20439                     cls : 'clear'
20440                 });
20441                 
20442                 cfg.cn[0].cn.push(bullets);
20443             }
20444             
20445             if(this.showarrow){
20446                 cfg.cn[0].cn.push({
20447                     tag : 'div',
20448                     class : 'carousel-arrow',
20449                     cn : [
20450                         {
20451                             tag : 'div',
20452                             class : 'carousel-prev',
20453                             cn : [
20454                                 {
20455                                     tag : 'i',
20456                                     class : 'fa fa-chevron-left'
20457                                 }
20458                             ]
20459                         },
20460                         {
20461                             tag : 'div',
20462                             class : 'carousel-next',
20463                             cn : [
20464                                 {
20465                                     tag : 'i',
20466                                     class : 'fa fa-chevron-right'
20467                                 }
20468                             ]
20469                         }
20470                     ]
20471                 });
20472             }
20473             
20474         }
20475         
20476         return cfg;
20477     },
20478     
20479     initEvents:  function()
20480     {
20481 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20482 //            this.el.on("touchstart", this.onTouchStart, this);
20483 //        }
20484         
20485         if(this.autoslide){
20486             var _this = this;
20487             
20488             this.slideFn = window.setInterval(function() {
20489                 _this.showPanelNext();
20490             }, this.timer);
20491         }
20492         
20493         if(this.showarrow){
20494             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20495             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20496         }
20497         
20498         
20499     },
20500     
20501 //    onTouchStart : function(e, el, o)
20502 //    {
20503 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20504 //            return;
20505 //        }
20506 //        
20507 //        this.showPanelNext();
20508 //    },
20509     
20510     
20511     getChildContainer : function()
20512     {
20513         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20514     },
20515     
20516     /**
20517     * register a Navigation item
20518     * @param {Roo.bootstrap.NavItem} the navitem to add
20519     */
20520     register : function(item)
20521     {
20522         this.tabs.push( item);
20523         item.navId = this.navId; // not really needed..
20524         this.addBullet();
20525     
20526     },
20527     
20528     getActivePanel : function()
20529     {
20530         var r = false;
20531         Roo.each(this.tabs, function(t) {
20532             if (t.active) {
20533                 r = t;
20534                 return false;
20535             }
20536             return null;
20537         });
20538         return r;
20539         
20540     },
20541     getPanelByName : function(n)
20542     {
20543         var r = false;
20544         Roo.each(this.tabs, function(t) {
20545             if (t.tabId == n) {
20546                 r = t;
20547                 return false;
20548             }
20549             return null;
20550         });
20551         return r;
20552     },
20553     indexOfPanel : function(p)
20554     {
20555         var r = false;
20556         Roo.each(this.tabs, function(t,i) {
20557             if (t.tabId == p.tabId) {
20558                 r = i;
20559                 return false;
20560             }
20561             return null;
20562         });
20563         return r;
20564     },
20565     /**
20566      * show a specific panel
20567      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20568      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20569      */
20570     showPanel : function (pan)
20571     {
20572         if(this.transition || typeof(pan) == 'undefined'){
20573             Roo.log("waiting for the transitionend");
20574             return false;
20575         }
20576         
20577         if (typeof(pan) == 'number') {
20578             pan = this.tabs[pan];
20579         }
20580         
20581         if (typeof(pan) == 'string') {
20582             pan = this.getPanelByName(pan);
20583         }
20584         
20585         var cur = this.getActivePanel();
20586         
20587         if(!pan || !cur){
20588             Roo.log('pan or acitve pan is undefined');
20589             return false;
20590         }
20591         
20592         if (pan.tabId == this.getActivePanel().tabId) {
20593             return true;
20594         }
20595         
20596         if (false === cur.fireEvent('beforedeactivate')) {
20597             return false;
20598         }
20599         
20600         if(this.bullets > 0 && !Roo.isTouch){
20601             this.setActiveBullet(this.indexOfPanel(pan));
20602         }
20603         
20604         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20605             
20606             //class="carousel-item carousel-item-next carousel-item-left"
20607             
20608             this.transition = true;
20609             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20610             var lr = dir == 'next' ? 'left' : 'right';
20611             pan.el.addClass(dir); // or prev
20612             pan.el.addClass('carousel-item-' + dir); // or prev
20613             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20614             cur.el.addClass(lr); // or right
20615             pan.el.addClass(lr);
20616             cur.el.addClass('carousel-item-' +lr); // or right
20617             pan.el.addClass('carousel-item-' +lr);
20618             
20619             
20620             var _this = this;
20621             cur.el.on('transitionend', function() {
20622                 Roo.log("trans end?");
20623                 
20624                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20625                 pan.setActive(true);
20626                 
20627                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20628                 cur.setActive(false);
20629                 
20630                 _this.transition = false;
20631                 
20632             }, this, { single:  true } );
20633             
20634             return true;
20635         }
20636         
20637         cur.setActive(false);
20638         pan.setActive(true);
20639         
20640         return true;
20641         
20642     },
20643     showPanelNext : function()
20644     {
20645         var i = this.indexOfPanel(this.getActivePanel());
20646         
20647         if (i >= this.tabs.length - 1 && !this.autoslide) {
20648             return;
20649         }
20650         
20651         if (i >= this.tabs.length - 1 && this.autoslide) {
20652             i = -1;
20653         }
20654         
20655         this.showPanel(this.tabs[i+1]);
20656     },
20657     
20658     showPanelPrev : function()
20659     {
20660         var i = this.indexOfPanel(this.getActivePanel());
20661         
20662         if (i  < 1 && !this.autoslide) {
20663             return;
20664         }
20665         
20666         if (i < 1 && this.autoslide) {
20667             i = this.tabs.length;
20668         }
20669         
20670         this.showPanel(this.tabs[i-1]);
20671     },
20672     
20673     
20674     addBullet: function()
20675     {
20676         if(!this.bullets || Roo.isTouch){
20677             return;
20678         }
20679         var ctr = this.el.select('.carousel-bullets',true).first();
20680         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20681         var bullet = ctr.createChild({
20682             cls : 'bullet bullet-' + i
20683         },ctr.dom.lastChild);
20684         
20685         
20686         var _this = this;
20687         
20688         bullet.on('click', (function(e, el, o, ii, t){
20689
20690             e.preventDefault();
20691
20692             this.showPanel(ii);
20693
20694             if(this.autoslide && this.slideFn){
20695                 clearInterval(this.slideFn);
20696                 this.slideFn = window.setInterval(function() {
20697                     _this.showPanelNext();
20698                 }, this.timer);
20699             }
20700
20701         }).createDelegate(this, [i, bullet], true));
20702                 
20703         
20704     },
20705      
20706     setActiveBullet : function(i)
20707     {
20708         if(Roo.isTouch){
20709             return;
20710         }
20711         
20712         Roo.each(this.el.select('.bullet', true).elements, function(el){
20713             el.removeClass('selected');
20714         });
20715
20716         var bullet = this.el.select('.bullet-' + i, true).first();
20717         
20718         if(!bullet){
20719             return;
20720         }
20721         
20722         bullet.addClass('selected');
20723     }
20724     
20725     
20726   
20727 });
20728
20729  
20730
20731  
20732  
20733 Roo.apply(Roo.bootstrap.TabGroup, {
20734     
20735     groups: {},
20736      /**
20737     * register a Navigation Group
20738     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20739     */
20740     register : function(navgrp)
20741     {
20742         this.groups[navgrp.navId] = navgrp;
20743         
20744     },
20745     /**
20746     * fetch a Navigation Group based on the navigation ID
20747     * if one does not exist , it will get created.
20748     * @param {string} the navgroup to add
20749     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20750     */
20751     get: function(navId) {
20752         if (typeof(this.groups[navId]) == 'undefined') {
20753             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20754         }
20755         return this.groups[navId] ;
20756     }
20757     
20758     
20759     
20760 });
20761
20762  /*
20763  * - LGPL
20764  *
20765  * TabPanel
20766  * 
20767  */
20768
20769 /**
20770  * @class Roo.bootstrap.TabPanel
20771  * @extends Roo.bootstrap.Component
20772  * Bootstrap TabPanel class
20773  * @cfg {Boolean} active panel active
20774  * @cfg {String} html panel content
20775  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20776  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20777  * @cfg {String} href click to link..
20778  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20779  * 
20780  * 
20781  * @constructor
20782  * Create a new TabPanel
20783  * @param {Object} config The config object
20784  */
20785
20786 Roo.bootstrap.TabPanel = function(config){
20787     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20788     this.addEvents({
20789         /**
20790              * @event changed
20791              * Fires when the active status changes
20792              * @param {Roo.bootstrap.TabPanel} this
20793              * @param {Boolean} state the new state
20794             
20795          */
20796         'changed': true,
20797         /**
20798              * @event beforedeactivate
20799              * Fires before a tab is de-activated - can be used to do validation on a form.
20800              * @param {Roo.bootstrap.TabPanel} this
20801              * @return {Boolean} false if there is an error
20802             
20803          */
20804         'beforedeactivate': true
20805      });
20806     
20807     this.tabId = this.tabId || Roo.id();
20808   
20809 };
20810
20811 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20812     
20813     active: false,
20814     html: false,
20815     tabId: false,
20816     navId : false,
20817     href : '',
20818     touchSlide : false,
20819     getAutoCreate : function(){
20820         
20821         
20822         var cfg = {
20823             tag: 'div',
20824             // item is needed for carousel - not sure if it has any effect otherwise
20825             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20826             html: this.html || ''
20827         };
20828         
20829         if(this.active){
20830             cfg.cls += ' active';
20831         }
20832         
20833         if(this.tabId){
20834             cfg.tabId = this.tabId;
20835         }
20836         
20837         
20838         
20839         return cfg;
20840     },
20841     
20842     initEvents:  function()
20843     {
20844         var p = this.parent();
20845         
20846         this.navId = this.navId || p.navId;
20847         
20848         if (typeof(this.navId) != 'undefined') {
20849             // not really needed.. but just in case.. parent should be a NavGroup.
20850             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20851             
20852             tg.register(this);
20853             
20854             var i = tg.tabs.length - 1;
20855             
20856             if(this.active && tg.bullets > 0 && i < tg.bullets){
20857                 tg.setActiveBullet(i);
20858             }
20859         }
20860         
20861         this.el.on('click', this.onClick, this);
20862         
20863         if(Roo.isTouch && this.touchSlide){
20864             this.el.on("touchstart", this.onTouchStart, this);
20865             this.el.on("touchmove", this.onTouchMove, this);
20866             this.el.on("touchend", this.onTouchEnd, this);
20867         }
20868         
20869     },
20870     
20871     onRender : function(ct, position)
20872     {
20873         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20874     },
20875     
20876     setActive : function(state)
20877     {
20878         Roo.log("panel - set active " + this.tabId + "=" + state);
20879         
20880         this.active = state;
20881         if (!state) {
20882             this.el.removeClass('active');
20883             
20884         } else  if (!this.el.hasClass('active')) {
20885             this.el.addClass('active');
20886         }
20887         
20888         this.fireEvent('changed', this, state);
20889     },
20890     
20891     onClick : function(e)
20892     {
20893         e.preventDefault();
20894         
20895         if(!this.href.length){
20896             return;
20897         }
20898         
20899         window.location.href = this.href;
20900     },
20901     
20902     startX : 0,
20903     startY : 0,
20904     endX : 0,
20905     endY : 0,
20906     swiping : false,
20907     
20908     onTouchStart : function(e)
20909     {
20910         this.swiping = false;
20911         
20912         this.startX = e.browserEvent.touches[0].clientX;
20913         this.startY = e.browserEvent.touches[0].clientY;
20914     },
20915     
20916     onTouchMove : function(e)
20917     {
20918         this.swiping = true;
20919         
20920         this.endX = e.browserEvent.touches[0].clientX;
20921         this.endY = e.browserEvent.touches[0].clientY;
20922     },
20923     
20924     onTouchEnd : function(e)
20925     {
20926         if(!this.swiping){
20927             this.onClick(e);
20928             return;
20929         }
20930         
20931         var tabGroup = this.parent();
20932         
20933         if(this.endX > this.startX){ // swiping right
20934             tabGroup.showPanelPrev();
20935             return;
20936         }
20937         
20938         if(this.startX > this.endX){ // swiping left
20939             tabGroup.showPanelNext();
20940             return;
20941         }
20942     }
20943     
20944     
20945 });
20946  
20947
20948  
20949
20950  /*
20951  * - LGPL
20952  *
20953  * DateField
20954  * 
20955  */
20956
20957 /**
20958  * @class Roo.bootstrap.DateField
20959  * @extends Roo.bootstrap.Input
20960  * Bootstrap DateField class
20961  * @cfg {Number} weekStart default 0
20962  * @cfg {String} viewMode default empty, (months|years)
20963  * @cfg {String} minViewMode default empty, (months|years)
20964  * @cfg {Number} startDate default -Infinity
20965  * @cfg {Number} endDate default Infinity
20966  * @cfg {Boolean} todayHighlight default false
20967  * @cfg {Boolean} todayBtn default false
20968  * @cfg {Boolean} calendarWeeks default false
20969  * @cfg {Object} daysOfWeekDisabled default empty
20970  * @cfg {Boolean} singleMode default false (true | false)
20971  * 
20972  * @cfg {Boolean} keyboardNavigation default true
20973  * @cfg {String} language default en
20974  * 
20975  * @constructor
20976  * Create a new DateField
20977  * @param {Object} config The config object
20978  */
20979
20980 Roo.bootstrap.DateField = function(config){
20981     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20982      this.addEvents({
20983             /**
20984              * @event show
20985              * Fires when this field show.
20986              * @param {Roo.bootstrap.DateField} this
20987              * @param {Mixed} date The date value
20988              */
20989             show : true,
20990             /**
20991              * @event show
20992              * Fires when this field hide.
20993              * @param {Roo.bootstrap.DateField} this
20994              * @param {Mixed} date The date value
20995              */
20996             hide : true,
20997             /**
20998              * @event select
20999              * Fires when select a date.
21000              * @param {Roo.bootstrap.DateField} this
21001              * @param {Mixed} date The date value
21002              */
21003             select : true,
21004             /**
21005              * @event beforeselect
21006              * Fires when before select a date.
21007              * @param {Roo.bootstrap.DateField} this
21008              * @param {Mixed} date The date value
21009              */
21010             beforeselect : true
21011         });
21012 };
21013
21014 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21015     
21016     /**
21017      * @cfg {String} format
21018      * The default date format string which can be overriden for localization support.  The format must be
21019      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21020      */
21021     format : "m/d/y",
21022     /**
21023      * @cfg {String} altFormats
21024      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21025      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21026      */
21027     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21028     
21029     weekStart : 0,
21030     
21031     viewMode : '',
21032     
21033     minViewMode : '',
21034     
21035     todayHighlight : false,
21036     
21037     todayBtn: false,
21038     
21039     language: 'en',
21040     
21041     keyboardNavigation: true,
21042     
21043     calendarWeeks: false,
21044     
21045     startDate: -Infinity,
21046     
21047     endDate: Infinity,
21048     
21049     daysOfWeekDisabled: [],
21050     
21051     _events: [],
21052     
21053     singleMode : false,
21054     
21055     UTCDate: function()
21056     {
21057         return new Date(Date.UTC.apply(Date, arguments));
21058     },
21059     
21060     UTCToday: function()
21061     {
21062         var today = new Date();
21063         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21064     },
21065     
21066     getDate: function() {
21067             var d = this.getUTCDate();
21068             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21069     },
21070     
21071     getUTCDate: function() {
21072             return this.date;
21073     },
21074     
21075     setDate: function(d) {
21076             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21077     },
21078     
21079     setUTCDate: function(d) {
21080             this.date = d;
21081             this.setValue(this.formatDate(this.date));
21082     },
21083         
21084     onRender: function(ct, position)
21085     {
21086         
21087         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21088         
21089         this.language = this.language || 'en';
21090         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21091         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21092         
21093         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21094         this.format = this.format || 'm/d/y';
21095         this.isInline = false;
21096         this.isInput = true;
21097         this.component = this.el.select('.add-on', true).first() || false;
21098         this.component = (this.component && this.component.length === 0) ? false : this.component;
21099         this.hasInput = this.component && this.inputEl().length;
21100         
21101         if (typeof(this.minViewMode === 'string')) {
21102             switch (this.minViewMode) {
21103                 case 'months':
21104                     this.minViewMode = 1;
21105                     break;
21106                 case 'years':
21107                     this.minViewMode = 2;
21108                     break;
21109                 default:
21110                     this.minViewMode = 0;
21111                     break;
21112             }
21113         }
21114         
21115         if (typeof(this.viewMode === 'string')) {
21116             switch (this.viewMode) {
21117                 case 'months':
21118                     this.viewMode = 1;
21119                     break;
21120                 case 'years':
21121                     this.viewMode = 2;
21122                     break;
21123                 default:
21124                     this.viewMode = 0;
21125                     break;
21126             }
21127         }
21128                 
21129         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21130         
21131 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21132         
21133         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21134         
21135         this.picker().on('mousedown', this.onMousedown, this);
21136         this.picker().on('click', this.onClick, this);
21137         
21138         this.picker().addClass('datepicker-dropdown');
21139         
21140         this.startViewMode = this.viewMode;
21141         
21142         if(this.singleMode){
21143             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21144                 v.setVisibilityMode(Roo.Element.DISPLAY);
21145                 v.hide();
21146             });
21147             
21148             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21149                 v.setStyle('width', '189px');
21150             });
21151         }
21152         
21153         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21154             if(!this.calendarWeeks){
21155                 v.remove();
21156                 return;
21157             }
21158             
21159             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21160             v.attr('colspan', function(i, val){
21161                 return parseInt(val) + 1;
21162             });
21163         });
21164                         
21165         
21166         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21167         
21168         this.setStartDate(this.startDate);
21169         this.setEndDate(this.endDate);
21170         
21171         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21172         
21173         this.fillDow();
21174         this.fillMonths();
21175         this.update();
21176         this.showMode();
21177         
21178         if(this.isInline) {
21179             this.showPopup();
21180         }
21181     },
21182     
21183     picker : function()
21184     {
21185         return this.pickerEl;
21186 //        return this.el.select('.datepicker', true).first();
21187     },
21188     
21189     fillDow: function()
21190     {
21191         var dowCnt = this.weekStart;
21192         
21193         var dow = {
21194             tag: 'tr',
21195             cn: [
21196                 
21197             ]
21198         };
21199         
21200         if(this.calendarWeeks){
21201             dow.cn.push({
21202                 tag: 'th',
21203                 cls: 'cw',
21204                 html: '&nbsp;'
21205             })
21206         }
21207         
21208         while (dowCnt < this.weekStart + 7) {
21209             dow.cn.push({
21210                 tag: 'th',
21211                 cls: 'dow',
21212                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21213             });
21214         }
21215         
21216         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21217     },
21218     
21219     fillMonths: function()
21220     {    
21221         var i = 0;
21222         var months = this.picker().select('>.datepicker-months td', true).first();
21223         
21224         months.dom.innerHTML = '';
21225         
21226         while (i < 12) {
21227             var month = {
21228                 tag: 'span',
21229                 cls: 'month',
21230                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21231             };
21232             
21233             months.createChild(month);
21234         }
21235         
21236     },
21237     
21238     update: function()
21239     {
21240         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;
21241         
21242         if (this.date < this.startDate) {
21243             this.viewDate = new Date(this.startDate);
21244         } else if (this.date > this.endDate) {
21245             this.viewDate = new Date(this.endDate);
21246         } else {
21247             this.viewDate = new Date(this.date);
21248         }
21249         
21250         this.fill();
21251     },
21252     
21253     fill: function() 
21254     {
21255         var d = new Date(this.viewDate),
21256                 year = d.getUTCFullYear(),
21257                 month = d.getUTCMonth(),
21258                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21259                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21260                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21261                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21262                 currentDate = this.date && this.date.valueOf(),
21263                 today = this.UTCToday();
21264         
21265         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21266         
21267 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21268         
21269 //        this.picker.select('>tfoot th.today').
21270 //                                              .text(dates[this.language].today)
21271 //                                              .toggle(this.todayBtn !== false);
21272     
21273         this.updateNavArrows();
21274         this.fillMonths();
21275                                                 
21276         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21277         
21278         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21279          
21280         prevMonth.setUTCDate(day);
21281         
21282         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21283         
21284         var nextMonth = new Date(prevMonth);
21285         
21286         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21287         
21288         nextMonth = nextMonth.valueOf();
21289         
21290         var fillMonths = false;
21291         
21292         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21293         
21294         while(prevMonth.valueOf() <= nextMonth) {
21295             var clsName = '';
21296             
21297             if (prevMonth.getUTCDay() === this.weekStart) {
21298                 if(fillMonths){
21299                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21300                 }
21301                     
21302                 fillMonths = {
21303                     tag: 'tr',
21304                     cn: []
21305                 };
21306                 
21307                 if(this.calendarWeeks){
21308                     // ISO 8601: First week contains first thursday.
21309                     // ISO also states week starts on Monday, but we can be more abstract here.
21310                     var
21311                     // Start of current week: based on weekstart/current date
21312                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21313                     // Thursday of this week
21314                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21315                     // First Thursday of year, year from thursday
21316                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21317                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21318                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21319                     
21320                     fillMonths.cn.push({
21321                         tag: 'td',
21322                         cls: 'cw',
21323                         html: calWeek
21324                     });
21325                 }
21326             }
21327             
21328             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21329                 clsName += ' old';
21330             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21331                 clsName += ' new';
21332             }
21333             if (this.todayHighlight &&
21334                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21335                 prevMonth.getUTCMonth() == today.getMonth() &&
21336                 prevMonth.getUTCDate() == today.getDate()) {
21337                 clsName += ' today';
21338             }
21339             
21340             if (currentDate && prevMonth.valueOf() === currentDate) {
21341                 clsName += ' active';
21342             }
21343             
21344             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21345                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21346                     clsName += ' disabled';
21347             }
21348             
21349             fillMonths.cn.push({
21350                 tag: 'td',
21351                 cls: 'day ' + clsName,
21352                 html: prevMonth.getDate()
21353             });
21354             
21355             prevMonth.setDate(prevMonth.getDate()+1);
21356         }
21357           
21358         var currentYear = this.date && this.date.getUTCFullYear();
21359         var currentMonth = this.date && this.date.getUTCMonth();
21360         
21361         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21362         
21363         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21364             v.removeClass('active');
21365             
21366             if(currentYear === year && k === currentMonth){
21367                 v.addClass('active');
21368             }
21369             
21370             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21371                 v.addClass('disabled');
21372             }
21373             
21374         });
21375         
21376         
21377         year = parseInt(year/10, 10) * 10;
21378         
21379         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21380         
21381         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21382         
21383         year -= 1;
21384         for (var i = -1; i < 11; i++) {
21385             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21386                 tag: 'span',
21387                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21388                 html: year
21389             });
21390             
21391             year += 1;
21392         }
21393     },
21394     
21395     showMode: function(dir) 
21396     {
21397         if (dir) {
21398             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21399         }
21400         
21401         Roo.each(this.picker().select('>div',true).elements, function(v){
21402             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21403             v.hide();
21404         });
21405         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21406     },
21407     
21408     place: function()
21409     {
21410         if(this.isInline) {
21411             return;
21412         }
21413         
21414         this.picker().removeClass(['bottom', 'top']);
21415         
21416         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21417             /*
21418              * place to the top of element!
21419              *
21420              */
21421             
21422             this.picker().addClass('top');
21423             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21424             
21425             return;
21426         }
21427         
21428         this.picker().addClass('bottom');
21429         
21430         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21431     },
21432     
21433     parseDate : function(value)
21434     {
21435         if(!value || value instanceof Date){
21436             return value;
21437         }
21438         var v = Date.parseDate(value, this.format);
21439         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21440             v = Date.parseDate(value, 'Y-m-d');
21441         }
21442         if(!v && this.altFormats){
21443             if(!this.altFormatsArray){
21444                 this.altFormatsArray = this.altFormats.split("|");
21445             }
21446             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21447                 v = Date.parseDate(value, this.altFormatsArray[i]);
21448             }
21449         }
21450         return v;
21451     },
21452     
21453     formatDate : function(date, fmt)
21454     {   
21455         return (!date || !(date instanceof Date)) ?
21456         date : date.dateFormat(fmt || this.format);
21457     },
21458     
21459     onFocus : function()
21460     {
21461         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21462         this.showPopup();
21463     },
21464     
21465     onBlur : function()
21466     {
21467         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21468         
21469         var d = this.inputEl().getValue();
21470         
21471         this.setValue(d);
21472                 
21473         this.hidePopup();
21474     },
21475     
21476     showPopup : function()
21477     {
21478         this.picker().show();
21479         this.update();
21480         this.place();
21481         
21482         this.fireEvent('showpopup', this, this.date);
21483     },
21484     
21485     hidePopup : function()
21486     {
21487         if(this.isInline) {
21488             return;
21489         }
21490         this.picker().hide();
21491         this.viewMode = this.startViewMode;
21492         this.showMode();
21493         
21494         this.fireEvent('hidepopup', this, this.date);
21495         
21496     },
21497     
21498     onMousedown: function(e)
21499     {
21500         e.stopPropagation();
21501         e.preventDefault();
21502     },
21503     
21504     keyup: function(e)
21505     {
21506         Roo.bootstrap.DateField.superclass.keyup.call(this);
21507         this.update();
21508     },
21509
21510     setValue: function(v)
21511     {
21512         if(this.fireEvent('beforeselect', this, v) !== false){
21513             var d = new Date(this.parseDate(v) ).clearTime();
21514         
21515             if(isNaN(d.getTime())){
21516                 this.date = this.viewDate = '';
21517                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21518                 return;
21519             }
21520
21521             v = this.formatDate(d);
21522
21523             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21524
21525             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21526
21527             this.update();
21528
21529             this.fireEvent('select', this, this.date);
21530         }
21531     },
21532     
21533     getValue: function()
21534     {
21535         return this.formatDate(this.date);
21536     },
21537     
21538     fireKey: function(e)
21539     {
21540         if (!this.picker().isVisible()){
21541             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21542                 this.showPopup();
21543             }
21544             return;
21545         }
21546         
21547         var dateChanged = false,
21548         dir, day, month,
21549         newDate, newViewDate;
21550         
21551         switch(e.keyCode){
21552             case 27: // escape
21553                 this.hidePopup();
21554                 e.preventDefault();
21555                 break;
21556             case 37: // left
21557             case 39: // right
21558                 if (!this.keyboardNavigation) {
21559                     break;
21560                 }
21561                 dir = e.keyCode == 37 ? -1 : 1;
21562                 
21563                 if (e.ctrlKey){
21564                     newDate = this.moveYear(this.date, dir);
21565                     newViewDate = this.moveYear(this.viewDate, dir);
21566                 } else if (e.shiftKey){
21567                     newDate = this.moveMonth(this.date, dir);
21568                     newViewDate = this.moveMonth(this.viewDate, dir);
21569                 } else {
21570                     newDate = new Date(this.date);
21571                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21572                     newViewDate = new Date(this.viewDate);
21573                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21574                 }
21575                 if (this.dateWithinRange(newDate)){
21576                     this.date = newDate;
21577                     this.viewDate = newViewDate;
21578                     this.setValue(this.formatDate(this.date));
21579 //                    this.update();
21580                     e.preventDefault();
21581                     dateChanged = true;
21582                 }
21583                 break;
21584             case 38: // up
21585             case 40: // down
21586                 if (!this.keyboardNavigation) {
21587                     break;
21588                 }
21589                 dir = e.keyCode == 38 ? -1 : 1;
21590                 if (e.ctrlKey){
21591                     newDate = this.moveYear(this.date, dir);
21592                     newViewDate = this.moveYear(this.viewDate, dir);
21593                 } else if (e.shiftKey){
21594                     newDate = this.moveMonth(this.date, dir);
21595                     newViewDate = this.moveMonth(this.viewDate, dir);
21596                 } else {
21597                     newDate = new Date(this.date);
21598                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21599                     newViewDate = new Date(this.viewDate);
21600                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21601                 }
21602                 if (this.dateWithinRange(newDate)){
21603                     this.date = newDate;
21604                     this.viewDate = newViewDate;
21605                     this.setValue(this.formatDate(this.date));
21606 //                    this.update();
21607                     e.preventDefault();
21608                     dateChanged = true;
21609                 }
21610                 break;
21611             case 13: // enter
21612                 this.setValue(this.formatDate(this.date));
21613                 this.hidePopup();
21614                 e.preventDefault();
21615                 break;
21616             case 9: // tab
21617                 this.setValue(this.formatDate(this.date));
21618                 this.hidePopup();
21619                 break;
21620             case 16: // shift
21621             case 17: // ctrl
21622             case 18: // alt
21623                 break;
21624             default :
21625                 this.hidePopup();
21626                 
21627         }
21628     },
21629     
21630     
21631     onClick: function(e) 
21632     {
21633         e.stopPropagation();
21634         e.preventDefault();
21635         
21636         var target = e.getTarget();
21637         
21638         if(target.nodeName.toLowerCase() === 'i'){
21639             target = Roo.get(target).dom.parentNode;
21640         }
21641         
21642         var nodeName = target.nodeName;
21643         var className = target.className;
21644         var html = target.innerHTML;
21645         //Roo.log(nodeName);
21646         
21647         switch(nodeName.toLowerCase()) {
21648             case 'th':
21649                 switch(className) {
21650                     case 'switch':
21651                         this.showMode(1);
21652                         break;
21653                     case 'prev':
21654                     case 'next':
21655                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21656                         switch(this.viewMode){
21657                                 case 0:
21658                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21659                                         break;
21660                                 case 1:
21661                                 case 2:
21662                                         this.viewDate = this.moveYear(this.viewDate, dir);
21663                                         break;
21664                         }
21665                         this.fill();
21666                         break;
21667                     case 'today':
21668                         var date = new Date();
21669                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21670 //                        this.fill()
21671                         this.setValue(this.formatDate(this.date));
21672                         
21673                         this.hidePopup();
21674                         break;
21675                 }
21676                 break;
21677             case 'span':
21678                 if (className.indexOf('disabled') < 0) {
21679                     this.viewDate.setUTCDate(1);
21680                     if (className.indexOf('month') > -1) {
21681                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21682                     } else {
21683                         var year = parseInt(html, 10) || 0;
21684                         this.viewDate.setUTCFullYear(year);
21685                         
21686                     }
21687                     
21688                     if(this.singleMode){
21689                         this.setValue(this.formatDate(this.viewDate));
21690                         this.hidePopup();
21691                         return;
21692                     }
21693                     
21694                     this.showMode(-1);
21695                     this.fill();
21696                 }
21697                 break;
21698                 
21699             case 'td':
21700                 //Roo.log(className);
21701                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21702                     var day = parseInt(html, 10) || 1;
21703                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21704                         month = (this.viewDate || new Date()).getUTCMonth();
21705
21706                     if (className.indexOf('old') > -1) {
21707                         if(month === 0 ){
21708                             month = 11;
21709                             year -= 1;
21710                         }else{
21711                             month -= 1;
21712                         }
21713                     } else if (className.indexOf('new') > -1) {
21714                         if (month == 11) {
21715                             month = 0;
21716                             year += 1;
21717                         } else {
21718                             month += 1;
21719                         }
21720                     }
21721                     //Roo.log([year,month,day]);
21722                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21723                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21724 //                    this.fill();
21725                     //Roo.log(this.formatDate(this.date));
21726                     this.setValue(this.formatDate(this.date));
21727                     this.hidePopup();
21728                 }
21729                 break;
21730         }
21731     },
21732     
21733     setStartDate: function(startDate)
21734     {
21735         this.startDate = startDate || -Infinity;
21736         if (this.startDate !== -Infinity) {
21737             this.startDate = this.parseDate(this.startDate);
21738         }
21739         this.update();
21740         this.updateNavArrows();
21741     },
21742
21743     setEndDate: function(endDate)
21744     {
21745         this.endDate = endDate || Infinity;
21746         if (this.endDate !== Infinity) {
21747             this.endDate = this.parseDate(this.endDate);
21748         }
21749         this.update();
21750         this.updateNavArrows();
21751     },
21752     
21753     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21754     {
21755         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21756         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21757             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21758         }
21759         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21760             return parseInt(d, 10);
21761         });
21762         this.update();
21763         this.updateNavArrows();
21764     },
21765     
21766     updateNavArrows: function() 
21767     {
21768         if(this.singleMode){
21769             return;
21770         }
21771         
21772         var d = new Date(this.viewDate),
21773         year = d.getUTCFullYear(),
21774         month = d.getUTCMonth();
21775         
21776         Roo.each(this.picker().select('.prev', true).elements, function(v){
21777             v.show();
21778             switch (this.viewMode) {
21779                 case 0:
21780
21781                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21782                         v.hide();
21783                     }
21784                     break;
21785                 case 1:
21786                 case 2:
21787                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21788                         v.hide();
21789                     }
21790                     break;
21791             }
21792         });
21793         
21794         Roo.each(this.picker().select('.next', true).elements, function(v){
21795             v.show();
21796             switch (this.viewMode) {
21797                 case 0:
21798
21799                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21800                         v.hide();
21801                     }
21802                     break;
21803                 case 1:
21804                 case 2:
21805                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21806                         v.hide();
21807                     }
21808                     break;
21809             }
21810         })
21811     },
21812     
21813     moveMonth: function(date, dir)
21814     {
21815         if (!dir) {
21816             return date;
21817         }
21818         var new_date = new Date(date.valueOf()),
21819         day = new_date.getUTCDate(),
21820         month = new_date.getUTCMonth(),
21821         mag = Math.abs(dir),
21822         new_month, test;
21823         dir = dir > 0 ? 1 : -1;
21824         if (mag == 1){
21825             test = dir == -1
21826             // If going back one month, make sure month is not current month
21827             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21828             ? function(){
21829                 return new_date.getUTCMonth() == month;
21830             }
21831             // If going forward one month, make sure month is as expected
21832             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21833             : function(){
21834                 return new_date.getUTCMonth() != new_month;
21835             };
21836             new_month = month + dir;
21837             new_date.setUTCMonth(new_month);
21838             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21839             if (new_month < 0 || new_month > 11) {
21840                 new_month = (new_month + 12) % 12;
21841             }
21842         } else {
21843             // For magnitudes >1, move one month at a time...
21844             for (var i=0; i<mag; i++) {
21845                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21846                 new_date = this.moveMonth(new_date, dir);
21847             }
21848             // ...then reset the day, keeping it in the new month
21849             new_month = new_date.getUTCMonth();
21850             new_date.setUTCDate(day);
21851             test = function(){
21852                 return new_month != new_date.getUTCMonth();
21853             };
21854         }
21855         // Common date-resetting loop -- if date is beyond end of month, make it
21856         // end of month
21857         while (test()){
21858             new_date.setUTCDate(--day);
21859             new_date.setUTCMonth(new_month);
21860         }
21861         return new_date;
21862     },
21863
21864     moveYear: function(date, dir)
21865     {
21866         return this.moveMonth(date, dir*12);
21867     },
21868
21869     dateWithinRange: function(date)
21870     {
21871         return date >= this.startDate && date <= this.endDate;
21872     },
21873
21874     
21875     remove: function() 
21876     {
21877         this.picker().remove();
21878     },
21879     
21880     validateValue : function(value)
21881     {
21882         if(this.getVisibilityEl().hasClass('hidden')){
21883             return true;
21884         }
21885         
21886         if(value.length < 1)  {
21887             if(this.allowBlank){
21888                 return true;
21889             }
21890             return false;
21891         }
21892         
21893         if(value.length < this.minLength){
21894             return false;
21895         }
21896         if(value.length > this.maxLength){
21897             return false;
21898         }
21899         if(this.vtype){
21900             var vt = Roo.form.VTypes;
21901             if(!vt[this.vtype](value, this)){
21902                 return false;
21903             }
21904         }
21905         if(typeof this.validator == "function"){
21906             var msg = this.validator(value);
21907             if(msg !== true){
21908                 return false;
21909             }
21910         }
21911         
21912         if(this.regex && !this.regex.test(value)){
21913             return false;
21914         }
21915         
21916         if(typeof(this.parseDate(value)) == 'undefined'){
21917             return false;
21918         }
21919         
21920         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21921             return false;
21922         }      
21923         
21924         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21925             return false;
21926         } 
21927         
21928         
21929         return true;
21930     },
21931     
21932     reset : function()
21933     {
21934         this.date = this.viewDate = '';
21935         
21936         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21937     }
21938    
21939 });
21940
21941 Roo.apply(Roo.bootstrap.DateField,  {
21942     
21943     head : {
21944         tag: 'thead',
21945         cn: [
21946         {
21947             tag: 'tr',
21948             cn: [
21949             {
21950                 tag: 'th',
21951                 cls: 'prev',
21952                 html: '<i class="fa fa-arrow-left"/>'
21953             },
21954             {
21955                 tag: 'th',
21956                 cls: 'switch',
21957                 colspan: '5'
21958             },
21959             {
21960                 tag: 'th',
21961                 cls: 'next',
21962                 html: '<i class="fa fa-arrow-right"/>'
21963             }
21964
21965             ]
21966         }
21967         ]
21968     },
21969     
21970     content : {
21971         tag: 'tbody',
21972         cn: [
21973         {
21974             tag: 'tr',
21975             cn: [
21976             {
21977                 tag: 'td',
21978                 colspan: '7'
21979             }
21980             ]
21981         }
21982         ]
21983     },
21984     
21985     footer : {
21986         tag: 'tfoot',
21987         cn: [
21988         {
21989             tag: 'tr',
21990             cn: [
21991             {
21992                 tag: 'th',
21993                 colspan: '7',
21994                 cls: 'today'
21995             }
21996                     
21997             ]
21998         }
21999         ]
22000     },
22001     
22002     dates:{
22003         en: {
22004             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22005             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22006             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22007             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22008             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22009             today: "Today"
22010         }
22011     },
22012     
22013     modes: [
22014     {
22015         clsName: 'days',
22016         navFnc: 'Month',
22017         navStep: 1
22018     },
22019     {
22020         clsName: 'months',
22021         navFnc: 'FullYear',
22022         navStep: 1
22023     },
22024     {
22025         clsName: 'years',
22026         navFnc: 'FullYear',
22027         navStep: 10
22028     }]
22029 });
22030
22031 Roo.apply(Roo.bootstrap.DateField,  {
22032   
22033     template : {
22034         tag: 'div',
22035         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22036         cn: [
22037         {
22038             tag: 'div',
22039             cls: 'datepicker-days',
22040             cn: [
22041             {
22042                 tag: 'table',
22043                 cls: 'table-condensed',
22044                 cn:[
22045                 Roo.bootstrap.DateField.head,
22046                 {
22047                     tag: 'tbody'
22048                 },
22049                 Roo.bootstrap.DateField.footer
22050                 ]
22051             }
22052             ]
22053         },
22054         {
22055             tag: 'div',
22056             cls: 'datepicker-months',
22057             cn: [
22058             {
22059                 tag: 'table',
22060                 cls: 'table-condensed',
22061                 cn:[
22062                 Roo.bootstrap.DateField.head,
22063                 Roo.bootstrap.DateField.content,
22064                 Roo.bootstrap.DateField.footer
22065                 ]
22066             }
22067             ]
22068         },
22069         {
22070             tag: 'div',
22071             cls: 'datepicker-years',
22072             cn: [
22073             {
22074                 tag: 'table',
22075                 cls: 'table-condensed',
22076                 cn:[
22077                 Roo.bootstrap.DateField.head,
22078                 Roo.bootstrap.DateField.content,
22079                 Roo.bootstrap.DateField.footer
22080                 ]
22081             }
22082             ]
22083         }
22084         ]
22085     }
22086 });
22087
22088  
22089
22090  /*
22091  * - LGPL
22092  *
22093  * TimeField
22094  * 
22095  */
22096
22097 /**
22098  * @class Roo.bootstrap.TimeField
22099  * @extends Roo.bootstrap.Input
22100  * Bootstrap DateField class
22101  * 
22102  * 
22103  * @constructor
22104  * Create a new TimeField
22105  * @param {Object} config The config object
22106  */
22107
22108 Roo.bootstrap.TimeField = function(config){
22109     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22110     this.addEvents({
22111             /**
22112              * @event show
22113              * Fires when this field show.
22114              * @param {Roo.bootstrap.DateField} thisthis
22115              * @param {Mixed} date The date value
22116              */
22117             show : true,
22118             /**
22119              * @event show
22120              * Fires when this field hide.
22121              * @param {Roo.bootstrap.DateField} this
22122              * @param {Mixed} date The date value
22123              */
22124             hide : true,
22125             /**
22126              * @event select
22127              * Fires when select a date.
22128              * @param {Roo.bootstrap.DateField} this
22129              * @param {Mixed} date The date value
22130              */
22131             select : true
22132         });
22133 };
22134
22135 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22136     
22137     /**
22138      * @cfg {String} format
22139      * The default time format string which can be overriden for localization support.  The format must be
22140      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22141      */
22142     format : "H:i",
22143
22144     getAutoCreate : function()
22145     {
22146         this.after = '<i class="fa far fa-clock"></i>';
22147         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22148         
22149          
22150     },
22151     onRender: function(ct, position)
22152     {
22153         
22154         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22155                 
22156         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22157         
22158         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22159         
22160         this.pop = this.picker().select('>.datepicker-time',true).first();
22161         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22162         
22163         this.picker().on('mousedown', this.onMousedown, this);
22164         this.picker().on('click', this.onClick, this);
22165         
22166         this.picker().addClass('datepicker-dropdown');
22167     
22168         this.fillTime();
22169         this.update();
22170             
22171         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22172         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22173         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22174         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22175         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22176         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22177
22178     },
22179     
22180     fireKey: function(e){
22181         if (!this.picker().isVisible()){
22182             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22183                 this.show();
22184             }
22185             return;
22186         }
22187
22188         e.preventDefault();
22189         
22190         switch(e.keyCode){
22191             case 27: // escape
22192                 this.hide();
22193                 break;
22194             case 37: // left
22195             case 39: // right
22196                 this.onTogglePeriod();
22197                 break;
22198             case 38: // up
22199                 this.onIncrementMinutes();
22200                 break;
22201             case 40: // down
22202                 this.onDecrementMinutes();
22203                 break;
22204             case 13: // enter
22205             case 9: // tab
22206                 this.setTime();
22207                 break;
22208         }
22209     },
22210     
22211     onClick: function(e) {
22212         e.stopPropagation();
22213         e.preventDefault();
22214     },
22215     
22216     picker : function()
22217     {
22218         return this.pickerEl;
22219     },
22220     
22221     fillTime: function()
22222     {    
22223         var time = this.pop.select('tbody', true).first();
22224         
22225         time.dom.innerHTML = '';
22226         
22227         time.createChild({
22228             tag: 'tr',
22229             cn: [
22230                 {
22231                     tag: 'td',
22232                     cn: [
22233                         {
22234                             tag: 'a',
22235                             href: '#',
22236                             cls: 'btn',
22237                             cn: [
22238                                 {
22239                                     tag: 'i',
22240                                     cls: 'hours-up fa fas fa-chevron-up'
22241                                 }
22242                             ]
22243                         } 
22244                     ]
22245                 },
22246                 {
22247                     tag: 'td',
22248                     cls: 'separator'
22249                 },
22250                 {
22251                     tag: 'td',
22252                     cn: [
22253                         {
22254                             tag: 'a',
22255                             href: '#',
22256                             cls: 'btn',
22257                             cn: [
22258                                 {
22259                                     tag: 'i',
22260                                     cls: 'minutes-up fa fas fa-chevron-up'
22261                                 }
22262                             ]
22263                         }
22264                     ]
22265                 },
22266                 {
22267                     tag: 'td',
22268                     cls: 'separator'
22269                 }
22270             ]
22271         });
22272         
22273         time.createChild({
22274             tag: 'tr',
22275             cn: [
22276                 {
22277                     tag: 'td',
22278                     cn: [
22279                         {
22280                             tag: 'span',
22281                             cls: 'timepicker-hour',
22282                             html: '00'
22283                         }  
22284                     ]
22285                 },
22286                 {
22287                     tag: 'td',
22288                     cls: 'separator',
22289                     html: ':'
22290                 },
22291                 {
22292                     tag: 'td',
22293                     cn: [
22294                         {
22295                             tag: 'span',
22296                             cls: 'timepicker-minute',
22297                             html: '00'
22298                         }  
22299                     ]
22300                 },
22301                 {
22302                     tag: 'td',
22303                     cls: 'separator'
22304                 },
22305                 {
22306                     tag: 'td',
22307                     cn: [
22308                         {
22309                             tag: 'button',
22310                             type: 'button',
22311                             cls: 'btn btn-primary period',
22312                             html: 'AM'
22313                             
22314                         }
22315                     ]
22316                 }
22317             ]
22318         });
22319         
22320         time.createChild({
22321             tag: 'tr',
22322             cn: [
22323                 {
22324                     tag: 'td',
22325                     cn: [
22326                         {
22327                             tag: 'a',
22328                             href: '#',
22329                             cls: 'btn',
22330                             cn: [
22331                                 {
22332                                     tag: 'span',
22333                                     cls: 'hours-down fa fas fa-chevron-down'
22334                                 }
22335                             ]
22336                         }
22337                     ]
22338                 },
22339                 {
22340                     tag: 'td',
22341                     cls: 'separator'
22342                 },
22343                 {
22344                     tag: 'td',
22345                     cn: [
22346                         {
22347                             tag: 'a',
22348                             href: '#',
22349                             cls: 'btn',
22350                             cn: [
22351                                 {
22352                                     tag: 'span',
22353                                     cls: 'minutes-down fa fas fa-chevron-down'
22354                                 }
22355                             ]
22356                         }
22357                     ]
22358                 },
22359                 {
22360                     tag: 'td',
22361                     cls: 'separator'
22362                 }
22363             ]
22364         });
22365         
22366     },
22367     
22368     update: function()
22369     {
22370         
22371         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22372         
22373         this.fill();
22374     },
22375     
22376     fill: function() 
22377     {
22378         var hours = this.time.getHours();
22379         var minutes = this.time.getMinutes();
22380         var period = 'AM';
22381         
22382         if(hours > 11){
22383             period = 'PM';
22384         }
22385         
22386         if(hours == 0){
22387             hours = 12;
22388         }
22389         
22390         
22391         if(hours > 12){
22392             hours = hours - 12;
22393         }
22394         
22395         if(hours < 10){
22396             hours = '0' + hours;
22397         }
22398         
22399         if(minutes < 10){
22400             minutes = '0' + minutes;
22401         }
22402         
22403         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22404         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22405         this.pop.select('button', true).first().dom.innerHTML = period;
22406         
22407     },
22408     
22409     place: function()
22410     {   
22411         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22412         
22413         var cls = ['bottom'];
22414         
22415         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22416             cls.pop();
22417             cls.push('top');
22418         }
22419         
22420         cls.push('right');
22421         
22422         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22423             cls.pop();
22424             cls.push('left');
22425         }
22426         //this.picker().setXY(20000,20000);
22427         this.picker().addClass(cls.join('-'));
22428         
22429         var _this = this;
22430         
22431         Roo.each(cls, function(c){
22432             if(c == 'bottom'){
22433                 (function() {
22434                  //  
22435                 }).defer(200);
22436                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22437                 //_this.picker().setTop(_this.inputEl().getHeight());
22438                 return;
22439             }
22440             if(c == 'top'){
22441                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22442                 
22443                 //_this.picker().setTop(0 - _this.picker().getHeight());
22444                 return;
22445             }
22446             /*
22447             if(c == 'left'){
22448                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22449                 return;
22450             }
22451             if(c == 'right'){
22452                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22453                 return;
22454             }
22455             */
22456         });
22457         
22458     },
22459   
22460     onFocus : function()
22461     {
22462         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22463         this.show();
22464     },
22465     
22466     onBlur : function()
22467     {
22468         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22469         this.hide();
22470     },
22471     
22472     show : function()
22473     {
22474         this.picker().show();
22475         this.pop.show();
22476         this.update();
22477         this.place();
22478         
22479         this.fireEvent('show', this, this.date);
22480     },
22481     
22482     hide : function()
22483     {
22484         this.picker().hide();
22485         this.pop.hide();
22486         
22487         this.fireEvent('hide', this, this.date);
22488     },
22489     
22490     setTime : function()
22491     {
22492         this.hide();
22493         this.setValue(this.time.format(this.format));
22494         
22495         this.fireEvent('select', this, this.date);
22496         
22497         
22498     },
22499     
22500     onMousedown: function(e){
22501         e.stopPropagation();
22502         e.preventDefault();
22503     },
22504     
22505     onIncrementHours: function()
22506     {
22507         Roo.log('onIncrementHours');
22508         this.time = this.time.add(Date.HOUR, 1);
22509         this.update();
22510         
22511     },
22512     
22513     onDecrementHours: function()
22514     {
22515         Roo.log('onDecrementHours');
22516         this.time = this.time.add(Date.HOUR, -1);
22517         this.update();
22518     },
22519     
22520     onIncrementMinutes: function()
22521     {
22522         Roo.log('onIncrementMinutes');
22523         this.time = this.time.add(Date.MINUTE, 1);
22524         this.update();
22525     },
22526     
22527     onDecrementMinutes: function()
22528     {
22529         Roo.log('onDecrementMinutes');
22530         this.time = this.time.add(Date.MINUTE, -1);
22531         this.update();
22532     },
22533     
22534     onTogglePeriod: function()
22535     {
22536         Roo.log('onTogglePeriod');
22537         this.time = this.time.add(Date.HOUR, 12);
22538         this.update();
22539     }
22540     
22541    
22542 });
22543  
22544
22545 Roo.apply(Roo.bootstrap.TimeField,  {
22546   
22547     template : {
22548         tag: 'div',
22549         cls: 'datepicker dropdown-menu',
22550         cn: [
22551             {
22552                 tag: 'div',
22553                 cls: 'datepicker-time',
22554                 cn: [
22555                 {
22556                     tag: 'table',
22557                     cls: 'table-condensed',
22558                     cn:[
22559                         {
22560                             tag: 'tbody',
22561                             cn: [
22562                                 {
22563                                     tag: 'tr',
22564                                     cn: [
22565                                     {
22566                                         tag: 'td',
22567                                         colspan: '7'
22568                                     }
22569                                     ]
22570                                 }
22571                             ]
22572                         },
22573                         {
22574                             tag: 'tfoot',
22575                             cn: [
22576                                 {
22577                                     tag: 'tr',
22578                                     cn: [
22579                                     {
22580                                         tag: 'th',
22581                                         colspan: '7',
22582                                         cls: '',
22583                                         cn: [
22584                                             {
22585                                                 tag: 'button',
22586                                                 cls: 'btn btn-info ok',
22587                                                 html: 'OK'
22588                                             }
22589                                         ]
22590                                     }
22591                     
22592                                     ]
22593                                 }
22594                             ]
22595                         }
22596                     ]
22597                 }
22598                 ]
22599             }
22600         ]
22601     }
22602 });
22603
22604  
22605
22606  /*
22607  * - LGPL
22608  *
22609  * MonthField
22610  * 
22611  */
22612
22613 /**
22614  * @class Roo.bootstrap.MonthField
22615  * @extends Roo.bootstrap.Input
22616  * Bootstrap MonthField class
22617  * 
22618  * @cfg {String} language default en
22619  * 
22620  * @constructor
22621  * Create a new MonthField
22622  * @param {Object} config The config object
22623  */
22624
22625 Roo.bootstrap.MonthField = function(config){
22626     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22627     
22628     this.addEvents({
22629         /**
22630          * @event show
22631          * Fires when this field show.
22632          * @param {Roo.bootstrap.MonthField} this
22633          * @param {Mixed} date The date value
22634          */
22635         show : true,
22636         /**
22637          * @event show
22638          * Fires when this field hide.
22639          * @param {Roo.bootstrap.MonthField} this
22640          * @param {Mixed} date The date value
22641          */
22642         hide : true,
22643         /**
22644          * @event select
22645          * Fires when select a date.
22646          * @param {Roo.bootstrap.MonthField} this
22647          * @param {String} oldvalue The old value
22648          * @param {String} newvalue The new value
22649          */
22650         select : true
22651     });
22652 };
22653
22654 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22655     
22656     onRender: function(ct, position)
22657     {
22658         
22659         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22660         
22661         this.language = this.language || 'en';
22662         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22663         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22664         
22665         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22666         this.isInline = false;
22667         this.isInput = true;
22668         this.component = this.el.select('.add-on', true).first() || false;
22669         this.component = (this.component && this.component.length === 0) ? false : this.component;
22670         this.hasInput = this.component && this.inputEL().length;
22671         
22672         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22673         
22674         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22675         
22676         this.picker().on('mousedown', this.onMousedown, this);
22677         this.picker().on('click', this.onClick, this);
22678         
22679         this.picker().addClass('datepicker-dropdown');
22680         
22681         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22682             v.setStyle('width', '189px');
22683         });
22684         
22685         this.fillMonths();
22686         
22687         this.update();
22688         
22689         if(this.isInline) {
22690             this.show();
22691         }
22692         
22693     },
22694     
22695     setValue: function(v, suppressEvent)
22696     {   
22697         var o = this.getValue();
22698         
22699         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22700         
22701         this.update();
22702
22703         if(suppressEvent !== true){
22704             this.fireEvent('select', this, o, v);
22705         }
22706         
22707     },
22708     
22709     getValue: function()
22710     {
22711         return this.value;
22712     },
22713     
22714     onClick: function(e) 
22715     {
22716         e.stopPropagation();
22717         e.preventDefault();
22718         
22719         var target = e.getTarget();
22720         
22721         if(target.nodeName.toLowerCase() === 'i'){
22722             target = Roo.get(target).dom.parentNode;
22723         }
22724         
22725         var nodeName = target.nodeName;
22726         var className = target.className;
22727         var html = target.innerHTML;
22728         
22729         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22730             return;
22731         }
22732         
22733         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22734         
22735         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22736         
22737         this.hide();
22738                         
22739     },
22740     
22741     picker : function()
22742     {
22743         return this.pickerEl;
22744     },
22745     
22746     fillMonths: function()
22747     {    
22748         var i = 0;
22749         var months = this.picker().select('>.datepicker-months td', true).first();
22750         
22751         months.dom.innerHTML = '';
22752         
22753         while (i < 12) {
22754             var month = {
22755                 tag: 'span',
22756                 cls: 'month',
22757                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22758             };
22759             
22760             months.createChild(month);
22761         }
22762         
22763     },
22764     
22765     update: function()
22766     {
22767         var _this = this;
22768         
22769         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22770             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22771         }
22772         
22773         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22774             e.removeClass('active');
22775             
22776             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22777                 e.addClass('active');
22778             }
22779         })
22780     },
22781     
22782     place: function()
22783     {
22784         if(this.isInline) {
22785             return;
22786         }
22787         
22788         this.picker().removeClass(['bottom', 'top']);
22789         
22790         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22791             /*
22792              * place to the top of element!
22793              *
22794              */
22795             
22796             this.picker().addClass('top');
22797             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22798             
22799             return;
22800         }
22801         
22802         this.picker().addClass('bottom');
22803         
22804         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22805     },
22806     
22807     onFocus : function()
22808     {
22809         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22810         this.show();
22811     },
22812     
22813     onBlur : function()
22814     {
22815         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22816         
22817         var d = this.inputEl().getValue();
22818         
22819         this.setValue(d);
22820                 
22821         this.hide();
22822     },
22823     
22824     show : function()
22825     {
22826         this.picker().show();
22827         this.picker().select('>.datepicker-months', true).first().show();
22828         this.update();
22829         this.place();
22830         
22831         this.fireEvent('show', this, this.date);
22832     },
22833     
22834     hide : function()
22835     {
22836         if(this.isInline) {
22837             return;
22838         }
22839         this.picker().hide();
22840         this.fireEvent('hide', this, this.date);
22841         
22842     },
22843     
22844     onMousedown: function(e)
22845     {
22846         e.stopPropagation();
22847         e.preventDefault();
22848     },
22849     
22850     keyup: function(e)
22851     {
22852         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22853         this.update();
22854     },
22855
22856     fireKey: function(e)
22857     {
22858         if (!this.picker().isVisible()){
22859             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22860                 this.show();
22861             }
22862             return;
22863         }
22864         
22865         var dir;
22866         
22867         switch(e.keyCode){
22868             case 27: // escape
22869                 this.hide();
22870                 e.preventDefault();
22871                 break;
22872             case 37: // left
22873             case 39: // right
22874                 dir = e.keyCode == 37 ? -1 : 1;
22875                 
22876                 this.vIndex = this.vIndex + dir;
22877                 
22878                 if(this.vIndex < 0){
22879                     this.vIndex = 0;
22880                 }
22881                 
22882                 if(this.vIndex > 11){
22883                     this.vIndex = 11;
22884                 }
22885                 
22886                 if(isNaN(this.vIndex)){
22887                     this.vIndex = 0;
22888                 }
22889                 
22890                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22891                 
22892                 break;
22893             case 38: // up
22894             case 40: // down
22895                 
22896                 dir = e.keyCode == 38 ? -1 : 1;
22897                 
22898                 this.vIndex = this.vIndex + dir * 4;
22899                 
22900                 if(this.vIndex < 0){
22901                     this.vIndex = 0;
22902                 }
22903                 
22904                 if(this.vIndex > 11){
22905                     this.vIndex = 11;
22906                 }
22907                 
22908                 if(isNaN(this.vIndex)){
22909                     this.vIndex = 0;
22910                 }
22911                 
22912                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22913                 break;
22914                 
22915             case 13: // enter
22916                 
22917                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22918                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22919                 }
22920                 
22921                 this.hide();
22922                 e.preventDefault();
22923                 break;
22924             case 9: // tab
22925                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22926                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22927                 }
22928                 this.hide();
22929                 break;
22930             case 16: // shift
22931             case 17: // ctrl
22932             case 18: // alt
22933                 break;
22934             default :
22935                 this.hide();
22936                 
22937         }
22938     },
22939     
22940     remove: function() 
22941     {
22942         this.picker().remove();
22943     }
22944    
22945 });
22946
22947 Roo.apply(Roo.bootstrap.MonthField,  {
22948     
22949     content : {
22950         tag: 'tbody',
22951         cn: [
22952         {
22953             tag: 'tr',
22954             cn: [
22955             {
22956                 tag: 'td',
22957                 colspan: '7'
22958             }
22959             ]
22960         }
22961         ]
22962     },
22963     
22964     dates:{
22965         en: {
22966             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22967             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22968         }
22969     }
22970 });
22971
22972 Roo.apply(Roo.bootstrap.MonthField,  {
22973   
22974     template : {
22975         tag: 'div',
22976         cls: 'datepicker dropdown-menu roo-dynamic',
22977         cn: [
22978             {
22979                 tag: 'div',
22980                 cls: 'datepicker-months',
22981                 cn: [
22982                 {
22983                     tag: 'table',
22984                     cls: 'table-condensed',
22985                     cn:[
22986                         Roo.bootstrap.DateField.content
22987                     ]
22988                 }
22989                 ]
22990             }
22991         ]
22992     }
22993 });
22994
22995  
22996
22997  
22998  /*
22999  * - LGPL
23000  *
23001  * CheckBox
23002  * 
23003  */
23004
23005 /**
23006  * @class Roo.bootstrap.CheckBox
23007  * @extends Roo.bootstrap.Input
23008  * Bootstrap CheckBox class
23009  * 
23010  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23011  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23012  * @cfg {String} boxLabel The text that appears beside the checkbox
23013  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23014  * @cfg {Boolean} checked initnal the element
23015  * @cfg {Boolean} inline inline the element (default false)
23016  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23017  * @cfg {String} tooltip label tooltip
23018  * 
23019  * @constructor
23020  * Create a new CheckBox
23021  * @param {Object} config The config object
23022  */
23023
23024 Roo.bootstrap.CheckBox = function(config){
23025     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23026    
23027     this.addEvents({
23028         /**
23029         * @event check
23030         * Fires when the element is checked or unchecked.
23031         * @param {Roo.bootstrap.CheckBox} this This input
23032         * @param {Boolean} checked The new checked value
23033         */
23034        check : true,
23035        /**
23036         * @event click
23037         * Fires when the element is click.
23038         * @param {Roo.bootstrap.CheckBox} this This input
23039         */
23040        click : true
23041     });
23042     
23043 };
23044
23045 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23046   
23047     inputType: 'checkbox',
23048     inputValue: 1,
23049     valueOff: 0,
23050     boxLabel: false,
23051     checked: false,
23052     weight : false,
23053     inline: false,
23054     tooltip : '',
23055     
23056     // checkbox success does not make any sense really.. 
23057     invalidClass : "",
23058     validClass : "",
23059     
23060     
23061     getAutoCreate : function()
23062     {
23063         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23064         
23065         var id = Roo.id();
23066         
23067         var cfg = {};
23068         
23069         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23070         
23071         if(this.inline){
23072             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23073         }
23074         
23075         var input =  {
23076             tag: 'input',
23077             id : id,
23078             type : this.inputType,
23079             value : this.inputValue,
23080             cls : 'roo-' + this.inputType, //'form-box',
23081             placeholder : this.placeholder || ''
23082             
23083         };
23084         
23085         if(this.inputType != 'radio'){
23086             var hidden =  {
23087                 tag: 'input',
23088                 type : 'hidden',
23089                 cls : 'roo-hidden-value',
23090                 value : this.checked ? this.inputValue : this.valueOff
23091             };
23092         }
23093         
23094             
23095         if (this.weight) { // Validity check?
23096             cfg.cls += " " + this.inputType + "-" + this.weight;
23097         }
23098         
23099         if (this.disabled) {
23100             input.disabled=true;
23101         }
23102         
23103         if(this.checked){
23104             input.checked = this.checked;
23105         }
23106         
23107         if (this.name) {
23108             
23109             input.name = this.name;
23110             
23111             if(this.inputType != 'radio'){
23112                 hidden.name = this.name;
23113                 input.name = '_hidden_' + this.name;
23114             }
23115         }
23116         
23117         if (this.size) {
23118             input.cls += ' input-' + this.size;
23119         }
23120         
23121         var settings=this;
23122         
23123         ['xs','sm','md','lg'].map(function(size){
23124             if (settings[size]) {
23125                 cfg.cls += ' col-' + size + '-' + settings[size];
23126             }
23127         });
23128         
23129         var inputblock = input;
23130          
23131         if (this.before || this.after) {
23132             
23133             inputblock = {
23134                 cls : 'input-group',
23135                 cn :  [] 
23136             };
23137             
23138             if (this.before) {
23139                 inputblock.cn.push({
23140                     tag :'span',
23141                     cls : 'input-group-addon',
23142                     html : this.before
23143                 });
23144             }
23145             
23146             inputblock.cn.push(input);
23147             
23148             if(this.inputType != 'radio'){
23149                 inputblock.cn.push(hidden);
23150             }
23151             
23152             if (this.after) {
23153                 inputblock.cn.push({
23154                     tag :'span',
23155                     cls : 'input-group-addon',
23156                     html : this.after
23157                 });
23158             }
23159             
23160         }
23161         var boxLabelCfg = false;
23162         
23163         if(this.boxLabel){
23164            
23165             boxLabelCfg = {
23166                 tag: 'label',
23167                 //'for': id, // box label is handled by onclick - so no for...
23168                 cls: 'box-label',
23169                 html: this.boxLabel
23170             };
23171             if(this.tooltip){
23172                 boxLabelCfg.tooltip = this.tooltip;
23173             }
23174              
23175         }
23176         
23177         
23178         if (align ==='left' && this.fieldLabel.length) {
23179 //                Roo.log("left and has label");
23180             cfg.cn = [
23181                 {
23182                     tag: 'label',
23183                     'for' :  id,
23184                     cls : 'control-label',
23185                     html : this.fieldLabel
23186                 },
23187                 {
23188                     cls : "", 
23189                     cn: [
23190                         inputblock
23191                     ]
23192                 }
23193             ];
23194             
23195             if (boxLabelCfg) {
23196                 cfg.cn[1].cn.push(boxLabelCfg);
23197             }
23198             
23199             if(this.labelWidth > 12){
23200                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23201             }
23202             
23203             if(this.labelWidth < 13 && this.labelmd == 0){
23204                 this.labelmd = this.labelWidth;
23205             }
23206             
23207             if(this.labellg > 0){
23208                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23209                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23210             }
23211             
23212             if(this.labelmd > 0){
23213                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23214                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23215             }
23216             
23217             if(this.labelsm > 0){
23218                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23219                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23220             }
23221             
23222             if(this.labelxs > 0){
23223                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23224                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23225             }
23226             
23227         } else if ( this.fieldLabel.length) {
23228 //                Roo.log(" label");
23229                 cfg.cn = [
23230                    
23231                     {
23232                         tag: this.boxLabel ? 'span' : 'label',
23233                         'for': id,
23234                         cls: 'control-label box-input-label',
23235                         //cls : 'input-group-addon',
23236                         html : this.fieldLabel
23237                     },
23238                     
23239                     inputblock
23240                     
23241                 ];
23242                 if (boxLabelCfg) {
23243                     cfg.cn.push(boxLabelCfg);
23244                 }
23245
23246         } else {
23247             
23248 //                Roo.log(" no label && no align");
23249                 cfg.cn = [  inputblock ] ;
23250                 if (boxLabelCfg) {
23251                     cfg.cn.push(boxLabelCfg);
23252                 }
23253
23254                 
23255         }
23256         
23257        
23258         
23259         if(this.inputType != 'radio'){
23260             cfg.cn.push(hidden);
23261         }
23262         
23263         return cfg;
23264         
23265     },
23266     
23267     /**
23268      * return the real input element.
23269      */
23270     inputEl: function ()
23271     {
23272         return this.el.select('input.roo-' + this.inputType,true).first();
23273     },
23274     hiddenEl: function ()
23275     {
23276         return this.el.select('input.roo-hidden-value',true).first();
23277     },
23278     
23279     labelEl: function()
23280     {
23281         return this.el.select('label.control-label',true).first();
23282     },
23283     /* depricated... */
23284     
23285     label: function()
23286     {
23287         return this.labelEl();
23288     },
23289     
23290     boxLabelEl: function()
23291     {
23292         return this.el.select('label.box-label',true).first();
23293     },
23294     
23295     initEvents : function()
23296     {
23297 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23298         
23299         this.inputEl().on('click', this.onClick,  this);
23300         
23301         if (this.boxLabel) { 
23302             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23303         }
23304         
23305         this.startValue = this.getValue();
23306         
23307         if(this.groupId){
23308             Roo.bootstrap.CheckBox.register(this);
23309         }
23310     },
23311     
23312     onClick : function(e)
23313     {   
23314         if(this.fireEvent('click', this, e) !== false){
23315             this.setChecked(!this.checked);
23316         }
23317         
23318     },
23319     
23320     setChecked : function(state,suppressEvent)
23321     {
23322         this.startValue = this.getValue();
23323
23324         if(this.inputType == 'radio'){
23325             
23326             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23327                 e.dom.checked = false;
23328             });
23329             
23330             this.inputEl().dom.checked = true;
23331             
23332             this.inputEl().dom.value = this.inputValue;
23333             
23334             if(suppressEvent !== true){
23335                 this.fireEvent('check', this, true);
23336             }
23337             
23338             this.validate();
23339             
23340             return;
23341         }
23342         
23343         this.checked = state;
23344         
23345         this.inputEl().dom.checked = state;
23346         
23347         
23348         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23349         
23350         if(suppressEvent !== true){
23351             this.fireEvent('check', this, state);
23352         }
23353         
23354         this.validate();
23355     },
23356     
23357     getValue : function()
23358     {
23359         if(this.inputType == 'radio'){
23360             return this.getGroupValue();
23361         }
23362         
23363         return this.hiddenEl().dom.value;
23364         
23365     },
23366     
23367     getGroupValue : function()
23368     {
23369         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23370             return '';
23371         }
23372         
23373         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23374     },
23375     
23376     setValue : function(v,suppressEvent)
23377     {
23378         if(this.inputType == 'radio'){
23379             this.setGroupValue(v, suppressEvent);
23380             return;
23381         }
23382         
23383         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23384         
23385         this.validate();
23386     },
23387     
23388     setGroupValue : function(v, suppressEvent)
23389     {
23390         this.startValue = this.getValue();
23391         
23392         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23393             e.dom.checked = false;
23394             
23395             if(e.dom.value == v){
23396                 e.dom.checked = true;
23397             }
23398         });
23399         
23400         if(suppressEvent !== true){
23401             this.fireEvent('check', this, true);
23402         }
23403
23404         this.validate();
23405         
23406         return;
23407     },
23408     
23409     validate : function()
23410     {
23411         if(this.getVisibilityEl().hasClass('hidden')){
23412             return true;
23413         }
23414         
23415         if(
23416                 this.disabled || 
23417                 (this.inputType == 'radio' && this.validateRadio()) ||
23418                 (this.inputType == 'checkbox' && this.validateCheckbox())
23419         ){
23420             this.markValid();
23421             return true;
23422         }
23423         
23424         this.markInvalid();
23425         return false;
23426     },
23427     
23428     validateRadio : function()
23429     {
23430         if(this.getVisibilityEl().hasClass('hidden')){
23431             return true;
23432         }
23433         
23434         if(this.allowBlank){
23435             return true;
23436         }
23437         
23438         var valid = false;
23439         
23440         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23441             if(!e.dom.checked){
23442                 return;
23443             }
23444             
23445             valid = true;
23446             
23447             return false;
23448         });
23449         
23450         return valid;
23451     },
23452     
23453     validateCheckbox : function()
23454     {
23455         if(!this.groupId){
23456             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23457             //return (this.getValue() == this.inputValue) ? true : false;
23458         }
23459         
23460         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23461         
23462         if(!group){
23463             return false;
23464         }
23465         
23466         var r = false;
23467         
23468         for(var i in group){
23469             if(group[i].el.isVisible(true)){
23470                 r = false;
23471                 break;
23472             }
23473             
23474             r = true;
23475         }
23476         
23477         for(var i in group){
23478             if(r){
23479                 break;
23480             }
23481             
23482             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23483         }
23484         
23485         return r;
23486     },
23487     
23488     /**
23489      * Mark this field as valid
23490      */
23491     markValid : function()
23492     {
23493         var _this = this;
23494         
23495         this.fireEvent('valid', this);
23496         
23497         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23498         
23499         if(this.groupId){
23500             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23501         }
23502         
23503         if(label){
23504             label.markValid();
23505         }
23506
23507         if(this.inputType == 'radio'){
23508             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23509                 var fg = e.findParent('.form-group', false, true);
23510                 if (Roo.bootstrap.version == 3) {
23511                     fg.removeClass([_this.invalidClass, _this.validClass]);
23512                     fg.addClass(_this.validClass);
23513                 } else {
23514                     fg.removeClass(['is-valid', 'is-invalid']);
23515                     fg.addClass('is-valid');
23516                 }
23517             });
23518             
23519             return;
23520         }
23521
23522         if(!this.groupId){
23523             var fg = this.el.findParent('.form-group', false, true);
23524             if (Roo.bootstrap.version == 3) {
23525                 fg.removeClass([this.invalidClass, this.validClass]);
23526                 fg.addClass(this.validClass);
23527             } else {
23528                 fg.removeClass(['is-valid', 'is-invalid']);
23529                 fg.addClass('is-valid');
23530             }
23531             return;
23532         }
23533         
23534         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23535         
23536         if(!group){
23537             return;
23538         }
23539         
23540         for(var i in group){
23541             var fg = group[i].el.findParent('.form-group', false, true);
23542             if (Roo.bootstrap.version == 3) {
23543                 fg.removeClass([this.invalidClass, this.validClass]);
23544                 fg.addClass(this.validClass);
23545             } else {
23546                 fg.removeClass(['is-valid', 'is-invalid']);
23547                 fg.addClass('is-valid');
23548             }
23549         }
23550     },
23551     
23552      /**
23553      * Mark this field as invalid
23554      * @param {String} msg The validation message
23555      */
23556     markInvalid : function(msg)
23557     {
23558         if(this.allowBlank){
23559             return;
23560         }
23561         
23562         var _this = this;
23563         
23564         this.fireEvent('invalid', this, msg);
23565         
23566         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23567         
23568         if(this.groupId){
23569             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23570         }
23571         
23572         if(label){
23573             label.markInvalid();
23574         }
23575             
23576         if(this.inputType == 'radio'){
23577             
23578             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23579                 var fg = e.findParent('.form-group', false, true);
23580                 if (Roo.bootstrap.version == 3) {
23581                     fg.removeClass([_this.invalidClass, _this.validClass]);
23582                     fg.addClass(_this.invalidClass);
23583                 } else {
23584                     fg.removeClass(['is-invalid', 'is-valid']);
23585                     fg.addClass('is-invalid');
23586                 }
23587             });
23588             
23589             return;
23590         }
23591         
23592         if(!this.groupId){
23593             var fg = this.el.findParent('.form-group', false, true);
23594             if (Roo.bootstrap.version == 3) {
23595                 fg.removeClass([_this.invalidClass, _this.validClass]);
23596                 fg.addClass(_this.invalidClass);
23597             } else {
23598                 fg.removeClass(['is-invalid', 'is-valid']);
23599                 fg.addClass('is-invalid');
23600             }
23601             return;
23602         }
23603         
23604         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23605         
23606         if(!group){
23607             return;
23608         }
23609         
23610         for(var i in group){
23611             var fg = group[i].el.findParent('.form-group', false, true);
23612             if (Roo.bootstrap.version == 3) {
23613                 fg.removeClass([_this.invalidClass, _this.validClass]);
23614                 fg.addClass(_this.invalidClass);
23615             } else {
23616                 fg.removeClass(['is-invalid', 'is-valid']);
23617                 fg.addClass('is-invalid');
23618             }
23619         }
23620         
23621     },
23622     
23623     clearInvalid : function()
23624     {
23625         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23626         
23627         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23628         
23629         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23630         
23631         if (label && label.iconEl) {
23632             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23633             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23634         }
23635     },
23636     
23637     disable : function()
23638     {
23639         if(this.inputType != 'radio'){
23640             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23641             return;
23642         }
23643         
23644         var _this = this;
23645         
23646         if(this.rendered){
23647             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23648                 _this.getActionEl().addClass(this.disabledClass);
23649                 e.dom.disabled = true;
23650             });
23651         }
23652         
23653         this.disabled = true;
23654         this.fireEvent("disable", this);
23655         return this;
23656     },
23657
23658     enable : function()
23659     {
23660         if(this.inputType != 'radio'){
23661             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23662             return;
23663         }
23664         
23665         var _this = this;
23666         
23667         if(this.rendered){
23668             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23669                 _this.getActionEl().removeClass(this.disabledClass);
23670                 e.dom.disabled = false;
23671             });
23672         }
23673         
23674         this.disabled = false;
23675         this.fireEvent("enable", this);
23676         return this;
23677     },
23678     
23679     setBoxLabel : function(v)
23680     {
23681         this.boxLabel = v;
23682         
23683         if(this.rendered){
23684             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23685         }
23686     }
23687
23688 });
23689
23690 Roo.apply(Roo.bootstrap.CheckBox, {
23691     
23692     groups: {},
23693     
23694      /**
23695     * register a CheckBox Group
23696     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23697     */
23698     register : function(checkbox)
23699     {
23700         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23701             this.groups[checkbox.groupId] = {};
23702         }
23703         
23704         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23705             return;
23706         }
23707         
23708         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23709         
23710     },
23711     /**
23712     * fetch a CheckBox Group based on the group ID
23713     * @param {string} the group ID
23714     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23715     */
23716     get: function(groupId) {
23717         if (typeof(this.groups[groupId]) == 'undefined') {
23718             return false;
23719         }
23720         
23721         return this.groups[groupId] ;
23722     }
23723     
23724     
23725 });
23726 /*
23727  * - LGPL
23728  *
23729  * RadioItem
23730  * 
23731  */
23732
23733 /**
23734  * @class Roo.bootstrap.Radio
23735  * @extends Roo.bootstrap.Component
23736  * Bootstrap Radio class
23737  * @cfg {String} boxLabel - the label associated
23738  * @cfg {String} value - the value of radio
23739  * 
23740  * @constructor
23741  * Create a new Radio
23742  * @param {Object} config The config object
23743  */
23744 Roo.bootstrap.Radio = function(config){
23745     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23746     
23747 };
23748
23749 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23750     
23751     boxLabel : '',
23752     
23753     value : '',
23754     
23755     getAutoCreate : function()
23756     {
23757         var cfg = {
23758             tag : 'div',
23759             cls : 'form-group radio',
23760             cn : [
23761                 {
23762                     tag : 'label',
23763                     cls : 'box-label',
23764                     html : this.boxLabel
23765                 }
23766             ]
23767         };
23768         
23769         return cfg;
23770     },
23771     
23772     initEvents : function() 
23773     {
23774         this.parent().register(this);
23775         
23776         this.el.on('click', this.onClick, this);
23777         
23778     },
23779     
23780     onClick : function(e)
23781     {
23782         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23783             this.setChecked(true);
23784         }
23785     },
23786     
23787     setChecked : function(state, suppressEvent)
23788     {
23789         this.parent().setValue(this.value, suppressEvent);
23790         
23791     },
23792     
23793     setBoxLabel : function(v)
23794     {
23795         this.boxLabel = v;
23796         
23797         if(this.rendered){
23798             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23799         }
23800     }
23801     
23802 });
23803  
23804
23805  /*
23806  * - LGPL
23807  *
23808  * Input
23809  * 
23810  */
23811
23812 /**
23813  * @class Roo.bootstrap.SecurePass
23814  * @extends Roo.bootstrap.Input
23815  * Bootstrap SecurePass class
23816  *
23817  * 
23818  * @constructor
23819  * Create a new SecurePass
23820  * @param {Object} config The config object
23821  */
23822  
23823 Roo.bootstrap.SecurePass = function (config) {
23824     // these go here, so the translation tool can replace them..
23825     this.errors = {
23826         PwdEmpty: "Please type a password, and then retype it to confirm.",
23827         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23828         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23829         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23830         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23831         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23832         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23833         TooWeak: "Your password is Too Weak."
23834     },
23835     this.meterLabel = "Password strength:";
23836     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23837     this.meterClass = [
23838         "roo-password-meter-tooweak", 
23839         "roo-password-meter-weak", 
23840         "roo-password-meter-medium", 
23841         "roo-password-meter-strong", 
23842         "roo-password-meter-grey"
23843     ];
23844     
23845     this.errors = {};
23846     
23847     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23848 }
23849
23850 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23851     /**
23852      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23853      * {
23854      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23855      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23856      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23857      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23858      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23859      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23860      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23861      * })
23862      */
23863     // private
23864     
23865     meterWidth: 300,
23866     errorMsg :'',    
23867     errors: false,
23868     imageRoot: '/',
23869     /**
23870      * @cfg {String/Object} Label for the strength meter (defaults to
23871      * 'Password strength:')
23872      */
23873     // private
23874     meterLabel: '',
23875     /**
23876      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23877      * ['Weak', 'Medium', 'Strong'])
23878      */
23879     // private    
23880     pwdStrengths: false,    
23881     // private
23882     strength: 0,
23883     // private
23884     _lastPwd: null,
23885     // private
23886     kCapitalLetter: 0,
23887     kSmallLetter: 1,
23888     kDigit: 2,
23889     kPunctuation: 3,
23890     
23891     insecure: false,
23892     // private
23893     initEvents: function ()
23894     {
23895         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23896
23897         if (this.el.is('input[type=password]') && Roo.isSafari) {
23898             this.el.on('keydown', this.SafariOnKeyDown, this);
23899         }
23900
23901         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23902     },
23903     // private
23904     onRender: function (ct, position)
23905     {
23906         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23907         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23908         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23909
23910         this.trigger.createChild({
23911                    cn: [
23912                     {
23913                     //id: 'PwdMeter',
23914                     tag: 'div',
23915                     cls: 'roo-password-meter-grey col-xs-12',
23916                     style: {
23917                         //width: 0,
23918                         //width: this.meterWidth + 'px'                                                
23919                         }
23920                     },
23921                     {                            
23922                          cls: 'roo-password-meter-text'                          
23923                     }
23924                 ]            
23925         });
23926
23927          
23928         if (this.hideTrigger) {
23929             this.trigger.setDisplayed(false);
23930         }
23931         this.setSize(this.width || '', this.height || '');
23932     },
23933     // private
23934     onDestroy: function ()
23935     {
23936         if (this.trigger) {
23937             this.trigger.removeAllListeners();
23938             this.trigger.remove();
23939         }
23940         if (this.wrap) {
23941             this.wrap.remove();
23942         }
23943         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23944     },
23945     // private
23946     checkStrength: function ()
23947     {
23948         var pwd = this.inputEl().getValue();
23949         if (pwd == this._lastPwd) {
23950             return;
23951         }
23952
23953         var strength;
23954         if (this.ClientSideStrongPassword(pwd)) {
23955             strength = 3;
23956         } else if (this.ClientSideMediumPassword(pwd)) {
23957             strength = 2;
23958         } else if (this.ClientSideWeakPassword(pwd)) {
23959             strength = 1;
23960         } else {
23961             strength = 0;
23962         }
23963         
23964         Roo.log('strength1: ' + strength);
23965         
23966         //var pm = this.trigger.child('div/div/div').dom;
23967         var pm = this.trigger.child('div/div');
23968         pm.removeClass(this.meterClass);
23969         pm.addClass(this.meterClass[strength]);
23970                 
23971         
23972         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23973                 
23974         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23975         
23976         this._lastPwd = pwd;
23977     },
23978     reset: function ()
23979     {
23980         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23981         
23982         this._lastPwd = '';
23983         
23984         var pm = this.trigger.child('div/div');
23985         pm.removeClass(this.meterClass);
23986         pm.addClass('roo-password-meter-grey');        
23987         
23988         
23989         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23990         
23991         pt.innerHTML = '';
23992         this.inputEl().dom.type='password';
23993     },
23994     // private
23995     validateValue: function (value)
23996     {
23997         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23998             return false;
23999         }
24000         if (value.length == 0) {
24001             if (this.allowBlank) {
24002                 this.clearInvalid();
24003                 return true;
24004             }
24005
24006             this.markInvalid(this.errors.PwdEmpty);
24007             this.errorMsg = this.errors.PwdEmpty;
24008             return false;
24009         }
24010         
24011         if(this.insecure){
24012             return true;
24013         }
24014         
24015         if (!value.match(/[\x21-\x7e]+/)) {
24016             this.markInvalid(this.errors.PwdBadChar);
24017             this.errorMsg = this.errors.PwdBadChar;
24018             return false;
24019         }
24020         if (value.length < 6) {
24021             this.markInvalid(this.errors.PwdShort);
24022             this.errorMsg = this.errors.PwdShort;
24023             return false;
24024         }
24025         if (value.length > 16) {
24026             this.markInvalid(this.errors.PwdLong);
24027             this.errorMsg = this.errors.PwdLong;
24028             return false;
24029         }
24030         var strength;
24031         if (this.ClientSideStrongPassword(value)) {
24032             strength = 3;
24033         } else if (this.ClientSideMediumPassword(value)) {
24034             strength = 2;
24035         } else if (this.ClientSideWeakPassword(value)) {
24036             strength = 1;
24037         } else {
24038             strength = 0;
24039         }
24040
24041         
24042         if (strength < 2) {
24043             //this.markInvalid(this.errors.TooWeak);
24044             this.errorMsg = this.errors.TooWeak;
24045             //return false;
24046         }
24047         
24048         
24049         console.log('strength2: ' + strength);
24050         
24051         //var pm = this.trigger.child('div/div/div').dom;
24052         
24053         var pm = this.trigger.child('div/div');
24054         pm.removeClass(this.meterClass);
24055         pm.addClass(this.meterClass[strength]);
24056                 
24057         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24058                 
24059         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24060         
24061         this.errorMsg = ''; 
24062         return true;
24063     },
24064     // private
24065     CharacterSetChecks: function (type)
24066     {
24067         this.type = type;
24068         this.fResult = false;
24069     },
24070     // private
24071     isctype: function (character, type)
24072     {
24073         switch (type) {  
24074             case this.kCapitalLetter:
24075                 if (character >= 'A' && character <= 'Z') {
24076                     return true;
24077                 }
24078                 break;
24079             
24080             case this.kSmallLetter:
24081                 if (character >= 'a' && character <= 'z') {
24082                     return true;
24083                 }
24084                 break;
24085             
24086             case this.kDigit:
24087                 if (character >= '0' && character <= '9') {
24088                     return true;
24089                 }
24090                 break;
24091             
24092             case this.kPunctuation:
24093                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24094                     return true;
24095                 }
24096                 break;
24097             
24098             default:
24099                 return false;
24100         }
24101
24102     },
24103     // private
24104     IsLongEnough: function (pwd, size)
24105     {
24106         return !(pwd == null || isNaN(size) || pwd.length < size);
24107     },
24108     // private
24109     SpansEnoughCharacterSets: function (word, nb)
24110     {
24111         if (!this.IsLongEnough(word, nb))
24112         {
24113             return false;
24114         }
24115
24116         var characterSetChecks = new Array(
24117             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24118             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24119         );
24120         
24121         for (var index = 0; index < word.length; ++index) {
24122             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24123                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24124                     characterSetChecks[nCharSet].fResult = true;
24125                     break;
24126                 }
24127             }
24128         }
24129
24130         var nCharSets = 0;
24131         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24132             if (characterSetChecks[nCharSet].fResult) {
24133                 ++nCharSets;
24134             }
24135         }
24136
24137         if (nCharSets < nb) {
24138             return false;
24139         }
24140         return true;
24141     },
24142     // private
24143     ClientSideStrongPassword: function (pwd)
24144     {
24145         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24146     },
24147     // private
24148     ClientSideMediumPassword: function (pwd)
24149     {
24150         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24151     },
24152     // private
24153     ClientSideWeakPassword: function (pwd)
24154     {
24155         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24156     }
24157           
24158 })//<script type="text/javascript">
24159
24160 /*
24161  * Based  Ext JS Library 1.1.1
24162  * Copyright(c) 2006-2007, Ext JS, LLC.
24163  * LGPL
24164  *
24165  */
24166  
24167 /**
24168  * @class Roo.HtmlEditorCore
24169  * @extends Roo.Component
24170  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24171  *
24172  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24173  */
24174
24175 Roo.HtmlEditorCore = function(config){
24176     
24177     
24178     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24179     
24180     
24181     this.addEvents({
24182         /**
24183          * @event initialize
24184          * Fires when the editor is fully initialized (including the iframe)
24185          * @param {Roo.HtmlEditorCore} this
24186          */
24187         initialize: true,
24188         /**
24189          * @event activate
24190          * Fires when the editor is first receives the focus. Any insertion must wait
24191          * until after this event.
24192          * @param {Roo.HtmlEditorCore} this
24193          */
24194         activate: true,
24195          /**
24196          * @event beforesync
24197          * Fires before the textarea is updated with content from the editor iframe. Return false
24198          * to cancel the sync.
24199          * @param {Roo.HtmlEditorCore} this
24200          * @param {String} html
24201          */
24202         beforesync: true,
24203          /**
24204          * @event beforepush
24205          * Fires before the iframe editor is updated with content from the textarea. Return false
24206          * to cancel the push.
24207          * @param {Roo.HtmlEditorCore} this
24208          * @param {String} html
24209          */
24210         beforepush: true,
24211          /**
24212          * @event sync
24213          * Fires when the textarea is updated with content from the editor iframe.
24214          * @param {Roo.HtmlEditorCore} this
24215          * @param {String} html
24216          */
24217         sync: true,
24218          /**
24219          * @event push
24220          * Fires when the iframe editor is updated with content from the textarea.
24221          * @param {Roo.HtmlEditorCore} this
24222          * @param {String} html
24223          */
24224         push: true,
24225         
24226         /**
24227          * @event editorevent
24228          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24229          * @param {Roo.HtmlEditorCore} this
24230          */
24231         editorevent: true
24232         
24233     });
24234     
24235     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24236     
24237     // defaults : white / black...
24238     this.applyBlacklists();
24239     
24240     
24241     
24242 };
24243
24244
24245 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24246
24247
24248      /**
24249      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24250      */
24251     
24252     owner : false,
24253     
24254      /**
24255      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24256      *                        Roo.resizable.
24257      */
24258     resizable : false,
24259      /**
24260      * @cfg {Number} height (in pixels)
24261      */   
24262     height: 300,
24263    /**
24264      * @cfg {Number} width (in pixels)
24265      */   
24266     width: 500,
24267     
24268     /**
24269      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24270      * 
24271      */
24272     stylesheets: false,
24273     
24274     // id of frame..
24275     frameId: false,
24276     
24277     // private properties
24278     validationEvent : false,
24279     deferHeight: true,
24280     initialized : false,
24281     activated : false,
24282     sourceEditMode : false,
24283     onFocus : Roo.emptyFn,
24284     iframePad:3,
24285     hideMode:'offsets',
24286     
24287     clearUp: true,
24288     
24289     // blacklist + whitelisted elements..
24290     black: false,
24291     white: false,
24292      
24293     bodyCls : '',
24294
24295     /**
24296      * Protected method that will not generally be called directly. It
24297      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24298      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24299      */
24300     getDocMarkup : function(){
24301         // body styles..
24302         var st = '';
24303         
24304         // inherit styels from page...?? 
24305         if (this.stylesheets === false) {
24306             
24307             Roo.get(document.head).select('style').each(function(node) {
24308                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24309             });
24310             
24311             Roo.get(document.head).select('link').each(function(node) { 
24312                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24313             });
24314             
24315         } else if (!this.stylesheets.length) {
24316                 // simple..
24317                 st = '<style type="text/css">' +
24318                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24319                    '</style>';
24320         } else {
24321             for (var i in this.stylesheets) { 
24322                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24323             }
24324             
24325         }
24326         
24327         st +=  '<style type="text/css">' +
24328             'IMG { cursor: pointer } ' +
24329         '</style>';
24330
24331         var cls = 'roo-htmleditor-body';
24332         
24333         if(this.bodyCls.length){
24334             cls += ' ' + this.bodyCls;
24335         }
24336         
24337         return '<html><head>' + st  +
24338             //<style type="text/css">' +
24339             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24340             //'</style>' +
24341             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24342     },
24343
24344     // private
24345     onRender : function(ct, position)
24346     {
24347         var _t = this;
24348         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24349         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24350         
24351         
24352         this.el.dom.style.border = '0 none';
24353         this.el.dom.setAttribute('tabIndex', -1);
24354         this.el.addClass('x-hidden hide');
24355         
24356         
24357         
24358         if(Roo.isIE){ // fix IE 1px bogus margin
24359             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24360         }
24361        
24362         
24363         this.frameId = Roo.id();
24364         
24365          
24366         
24367         var iframe = this.owner.wrap.createChild({
24368             tag: 'iframe',
24369             cls: 'form-control', // bootstrap..
24370             id: this.frameId,
24371             name: this.frameId,
24372             frameBorder : 'no',
24373             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24374         }, this.el
24375         );
24376         
24377         
24378         this.iframe = iframe.dom;
24379
24380          this.assignDocWin();
24381         
24382         this.doc.designMode = 'on';
24383        
24384         this.doc.open();
24385         this.doc.write(this.getDocMarkup());
24386         this.doc.close();
24387
24388         
24389         var task = { // must defer to wait for browser to be ready
24390             run : function(){
24391                 //console.log("run task?" + this.doc.readyState);
24392                 this.assignDocWin();
24393                 if(this.doc.body || this.doc.readyState == 'complete'){
24394                     try {
24395                         this.doc.designMode="on";
24396                     } catch (e) {
24397                         return;
24398                     }
24399                     Roo.TaskMgr.stop(task);
24400                     this.initEditor.defer(10, this);
24401                 }
24402             },
24403             interval : 10,
24404             duration: 10000,
24405             scope: this
24406         };
24407         Roo.TaskMgr.start(task);
24408
24409     },
24410
24411     // private
24412     onResize : function(w, h)
24413     {
24414          Roo.log('resize: ' +w + ',' + h );
24415         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24416         if(!this.iframe){
24417             return;
24418         }
24419         if(typeof w == 'number'){
24420             
24421             this.iframe.style.width = w + 'px';
24422         }
24423         if(typeof h == 'number'){
24424             
24425             this.iframe.style.height = h + 'px';
24426             if(this.doc){
24427                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24428             }
24429         }
24430         
24431     },
24432
24433     /**
24434      * Toggles the editor between standard and source edit mode.
24435      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24436      */
24437     toggleSourceEdit : function(sourceEditMode){
24438         
24439         this.sourceEditMode = sourceEditMode === true;
24440         
24441         if(this.sourceEditMode){
24442  
24443             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24444             
24445         }else{
24446             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24447             //this.iframe.className = '';
24448             this.deferFocus();
24449         }
24450         //this.setSize(this.owner.wrap.getSize());
24451         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24452     },
24453
24454     
24455   
24456
24457     /**
24458      * Protected method that will not generally be called directly. If you need/want
24459      * custom HTML cleanup, this is the method you should override.
24460      * @param {String} html The HTML to be cleaned
24461      * return {String} The cleaned HTML
24462      */
24463     cleanHtml : function(html){
24464         html = String(html);
24465         if(html.length > 5){
24466             if(Roo.isSafari){ // strip safari nonsense
24467                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24468             }
24469         }
24470         if(html == '&nbsp;'){
24471             html = '';
24472         }
24473         return html;
24474     },
24475
24476     /**
24477      * HTML Editor -> Textarea
24478      * Protected method that will not generally be called directly. Syncs the contents
24479      * of the editor iframe with the textarea.
24480      */
24481     syncValue : function(){
24482         if(this.initialized){
24483             var bd = (this.doc.body || this.doc.documentElement);
24484             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24485             var html = bd.innerHTML;
24486             if(Roo.isSafari){
24487                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24488                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24489                 if(m && m[1]){
24490                     html = '<div style="'+m[0]+'">' + html + '</div>';
24491                 }
24492             }
24493             html = this.cleanHtml(html);
24494             // fix up the special chars.. normaly like back quotes in word...
24495             // however we do not want to do this with chinese..
24496             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24497                 
24498                 var cc = match.charCodeAt();
24499
24500                 // Get the character value, handling surrogate pairs
24501                 if (match.length == 2) {
24502                     // It's a surrogate pair, calculate the Unicode code point
24503                     var high = match.charCodeAt(0) - 0xD800;
24504                     var low  = match.charCodeAt(1) - 0xDC00;
24505                     cc = (high * 0x400) + low + 0x10000;
24506                 }  else if (
24507                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24508                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24509                     (cc >= 0xf900 && cc < 0xfb00 )
24510                 ) {
24511                         return match;
24512                 }  
24513          
24514                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24515                 return "&#" + cc + ";";
24516                 
24517                 
24518             });
24519             
24520             
24521              
24522             if(this.owner.fireEvent('beforesync', this, html) !== false){
24523                 this.el.dom.value = html;
24524                 this.owner.fireEvent('sync', this, html);
24525             }
24526         }
24527     },
24528
24529     /**
24530      * Protected method that will not generally be called directly. Pushes the value of the textarea
24531      * into the iframe editor.
24532      */
24533     pushValue : function(){
24534         if(this.initialized){
24535             var v = this.el.dom.value.trim();
24536             
24537 //            if(v.length < 1){
24538 //                v = '&#160;';
24539 //            }
24540             
24541             if(this.owner.fireEvent('beforepush', this, v) !== false){
24542                 var d = (this.doc.body || this.doc.documentElement);
24543                 d.innerHTML = v;
24544                 this.cleanUpPaste();
24545                 this.el.dom.value = d.innerHTML;
24546                 this.owner.fireEvent('push', this, v);
24547             }
24548         }
24549     },
24550
24551     // private
24552     deferFocus : function(){
24553         this.focus.defer(10, this);
24554     },
24555
24556     // doc'ed in Field
24557     focus : function(){
24558         if(this.win && !this.sourceEditMode){
24559             this.win.focus();
24560         }else{
24561             this.el.focus();
24562         }
24563     },
24564     
24565     assignDocWin: function()
24566     {
24567         var iframe = this.iframe;
24568         
24569          if(Roo.isIE){
24570             this.doc = iframe.contentWindow.document;
24571             this.win = iframe.contentWindow;
24572         } else {
24573 //            if (!Roo.get(this.frameId)) {
24574 //                return;
24575 //            }
24576 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24577 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24578             
24579             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24580                 return;
24581             }
24582             
24583             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24584             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24585         }
24586     },
24587     
24588     // private
24589     initEditor : function(){
24590         //console.log("INIT EDITOR");
24591         this.assignDocWin();
24592         
24593         
24594         
24595         this.doc.designMode="on";
24596         this.doc.open();
24597         this.doc.write(this.getDocMarkup());
24598         this.doc.close();
24599         
24600         var dbody = (this.doc.body || this.doc.documentElement);
24601         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24602         // this copies styles from the containing element into thsi one..
24603         // not sure why we need all of this..
24604         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24605         
24606         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24607         //ss['background-attachment'] = 'fixed'; // w3c
24608         dbody.bgProperties = 'fixed'; // ie
24609         //Roo.DomHelper.applyStyles(dbody, ss);
24610         Roo.EventManager.on(this.doc, {
24611             //'mousedown': this.onEditorEvent,
24612             'mouseup': this.onEditorEvent,
24613             'dblclick': this.onEditorEvent,
24614             'click': this.onEditorEvent,
24615             'keyup': this.onEditorEvent,
24616             buffer:100,
24617             scope: this
24618         });
24619         if(Roo.isGecko){
24620             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24621         }
24622         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24623             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24624         }
24625         this.initialized = true;
24626
24627         this.owner.fireEvent('initialize', this);
24628         this.pushValue();
24629     },
24630
24631     // private
24632     onDestroy : function(){
24633         
24634         
24635         
24636         if(this.rendered){
24637             
24638             //for (var i =0; i < this.toolbars.length;i++) {
24639             //    // fixme - ask toolbars for heights?
24640             //    this.toolbars[i].onDestroy();
24641            // }
24642             
24643             //this.wrap.dom.innerHTML = '';
24644             //this.wrap.remove();
24645         }
24646     },
24647
24648     // private
24649     onFirstFocus : function(){
24650         
24651         this.assignDocWin();
24652         
24653         
24654         this.activated = true;
24655          
24656     
24657         if(Roo.isGecko){ // prevent silly gecko errors
24658             this.win.focus();
24659             var s = this.win.getSelection();
24660             if(!s.focusNode || s.focusNode.nodeType != 3){
24661                 var r = s.getRangeAt(0);
24662                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24663                 r.collapse(true);
24664                 this.deferFocus();
24665             }
24666             try{
24667                 this.execCmd('useCSS', true);
24668                 this.execCmd('styleWithCSS', false);
24669             }catch(e){}
24670         }
24671         this.owner.fireEvent('activate', this);
24672     },
24673
24674     // private
24675     adjustFont: function(btn){
24676         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24677         //if(Roo.isSafari){ // safari
24678         //    adjust *= 2;
24679        // }
24680         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24681         if(Roo.isSafari){ // safari
24682             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24683             v =  (v < 10) ? 10 : v;
24684             v =  (v > 48) ? 48 : v;
24685             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24686             
24687         }
24688         
24689         
24690         v = Math.max(1, v+adjust);
24691         
24692         this.execCmd('FontSize', v  );
24693     },
24694
24695     onEditorEvent : function(e)
24696     {
24697         this.owner.fireEvent('editorevent', this, e);
24698       //  this.updateToolbar();
24699         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24700     },
24701
24702     insertTag : function(tg)
24703     {
24704         // could be a bit smarter... -> wrap the current selected tRoo..
24705         if (tg.toLowerCase() == 'span' ||
24706             tg.toLowerCase() == 'code' ||
24707             tg.toLowerCase() == 'sup' ||
24708             tg.toLowerCase() == 'sub' 
24709             ) {
24710             
24711             range = this.createRange(this.getSelection());
24712             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24713             wrappingNode.appendChild(range.extractContents());
24714             range.insertNode(wrappingNode);
24715
24716             return;
24717             
24718             
24719             
24720         }
24721         this.execCmd("formatblock",   tg);
24722         
24723     },
24724     
24725     insertText : function(txt)
24726     {
24727         
24728         
24729         var range = this.createRange();
24730         range.deleteContents();
24731                //alert(Sender.getAttribute('label'));
24732                
24733         range.insertNode(this.doc.createTextNode(txt));
24734     } ,
24735     
24736      
24737
24738     /**
24739      * Executes a Midas editor command on the editor document and performs necessary focus and
24740      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24741      * @param {String} cmd The Midas command
24742      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24743      */
24744     relayCmd : function(cmd, value){
24745         this.win.focus();
24746         this.execCmd(cmd, value);
24747         this.owner.fireEvent('editorevent', this);
24748         //this.updateToolbar();
24749         this.owner.deferFocus();
24750     },
24751
24752     /**
24753      * Executes a Midas editor command directly on the editor document.
24754      * For visual commands, you should use {@link #relayCmd} instead.
24755      * <b>This should only be called after the editor is initialized.</b>
24756      * @param {String} cmd The Midas command
24757      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24758      */
24759     execCmd : function(cmd, value){
24760         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24761         this.syncValue();
24762     },
24763  
24764  
24765    
24766     /**
24767      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24768      * to insert tRoo.
24769      * @param {String} text | dom node.. 
24770      */
24771     insertAtCursor : function(text)
24772     {
24773         
24774         if(!this.activated){
24775             return;
24776         }
24777         /*
24778         if(Roo.isIE){
24779             this.win.focus();
24780             var r = this.doc.selection.createRange();
24781             if(r){
24782                 r.collapse(true);
24783                 r.pasteHTML(text);
24784                 this.syncValue();
24785                 this.deferFocus();
24786             
24787             }
24788             return;
24789         }
24790         */
24791         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24792             this.win.focus();
24793             
24794             
24795             // from jquery ui (MIT licenced)
24796             var range, node;
24797             var win = this.win;
24798             
24799             if (win.getSelection && win.getSelection().getRangeAt) {
24800                 range = win.getSelection().getRangeAt(0);
24801                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24802                 range.insertNode(node);
24803             } else if (win.document.selection && win.document.selection.createRange) {
24804                 // no firefox support
24805                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24806                 win.document.selection.createRange().pasteHTML(txt);
24807             } else {
24808                 // no firefox support
24809                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24810                 this.execCmd('InsertHTML', txt);
24811             } 
24812             
24813             this.syncValue();
24814             
24815             this.deferFocus();
24816         }
24817     },
24818  // private
24819     mozKeyPress : function(e){
24820         if(e.ctrlKey){
24821             var c = e.getCharCode(), cmd;
24822           
24823             if(c > 0){
24824                 c = String.fromCharCode(c).toLowerCase();
24825                 switch(c){
24826                     case 'b':
24827                         cmd = 'bold';
24828                         break;
24829                     case 'i':
24830                         cmd = 'italic';
24831                         break;
24832                     
24833                     case 'u':
24834                         cmd = 'underline';
24835                         break;
24836                     
24837                     case 'v':
24838                         this.cleanUpPaste.defer(100, this);
24839                         return;
24840                         
24841                 }
24842                 if(cmd){
24843                     this.win.focus();
24844                     this.execCmd(cmd);
24845                     this.deferFocus();
24846                     e.preventDefault();
24847                 }
24848                 
24849             }
24850         }
24851     },
24852
24853     // private
24854     fixKeys : function(){ // load time branching for fastest keydown performance
24855         if(Roo.isIE){
24856             return function(e){
24857                 var k = e.getKey(), r;
24858                 if(k == e.TAB){
24859                     e.stopEvent();
24860                     r = this.doc.selection.createRange();
24861                     if(r){
24862                         r.collapse(true);
24863                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24864                         this.deferFocus();
24865                     }
24866                     return;
24867                 }
24868                 
24869                 if(k == e.ENTER){
24870                     r = this.doc.selection.createRange();
24871                     if(r){
24872                         var target = r.parentElement();
24873                         if(!target || target.tagName.toLowerCase() != 'li'){
24874                             e.stopEvent();
24875                             r.pasteHTML('<br />');
24876                             r.collapse(false);
24877                             r.select();
24878                         }
24879                     }
24880                 }
24881                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24882                     this.cleanUpPaste.defer(100, this);
24883                     return;
24884                 }
24885                 
24886                 
24887             };
24888         }else if(Roo.isOpera){
24889             return function(e){
24890                 var k = e.getKey();
24891                 if(k == e.TAB){
24892                     e.stopEvent();
24893                     this.win.focus();
24894                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24895                     this.deferFocus();
24896                 }
24897                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24898                     this.cleanUpPaste.defer(100, this);
24899                     return;
24900                 }
24901                 
24902             };
24903         }else if(Roo.isSafari){
24904             return function(e){
24905                 var k = e.getKey();
24906                 
24907                 if(k == e.TAB){
24908                     e.stopEvent();
24909                     this.execCmd('InsertText','\t');
24910                     this.deferFocus();
24911                     return;
24912                 }
24913                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24914                     this.cleanUpPaste.defer(100, this);
24915                     return;
24916                 }
24917                 
24918              };
24919         }
24920     }(),
24921     
24922     getAllAncestors: function()
24923     {
24924         var p = this.getSelectedNode();
24925         var a = [];
24926         if (!p) {
24927             a.push(p); // push blank onto stack..
24928             p = this.getParentElement();
24929         }
24930         
24931         
24932         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24933             a.push(p);
24934             p = p.parentNode;
24935         }
24936         a.push(this.doc.body);
24937         return a;
24938     },
24939     lastSel : false,
24940     lastSelNode : false,
24941     
24942     
24943     getSelection : function() 
24944     {
24945         this.assignDocWin();
24946         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24947     },
24948     
24949     getSelectedNode: function() 
24950     {
24951         // this may only work on Gecko!!!
24952         
24953         // should we cache this!!!!
24954         
24955         
24956         
24957          
24958         var range = this.createRange(this.getSelection()).cloneRange();
24959         
24960         if (Roo.isIE) {
24961             var parent = range.parentElement();
24962             while (true) {
24963                 var testRange = range.duplicate();
24964                 testRange.moveToElementText(parent);
24965                 if (testRange.inRange(range)) {
24966                     break;
24967                 }
24968                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24969                     break;
24970                 }
24971                 parent = parent.parentElement;
24972             }
24973             return parent;
24974         }
24975         
24976         // is ancestor a text element.
24977         var ac =  range.commonAncestorContainer;
24978         if (ac.nodeType == 3) {
24979             ac = ac.parentNode;
24980         }
24981         
24982         var ar = ac.childNodes;
24983          
24984         var nodes = [];
24985         var other_nodes = [];
24986         var has_other_nodes = false;
24987         for (var i=0;i<ar.length;i++) {
24988             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24989                 continue;
24990             }
24991             // fullly contained node.
24992             
24993             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24994                 nodes.push(ar[i]);
24995                 continue;
24996             }
24997             
24998             // probably selected..
24999             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25000                 other_nodes.push(ar[i]);
25001                 continue;
25002             }
25003             // outer..
25004             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25005                 continue;
25006             }
25007             
25008             
25009             has_other_nodes = true;
25010         }
25011         if (!nodes.length && other_nodes.length) {
25012             nodes= other_nodes;
25013         }
25014         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25015             return false;
25016         }
25017         
25018         return nodes[0];
25019     },
25020     createRange: function(sel)
25021     {
25022         // this has strange effects when using with 
25023         // top toolbar - not sure if it's a great idea.
25024         //this.editor.contentWindow.focus();
25025         if (typeof sel != "undefined") {
25026             try {
25027                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25028             } catch(e) {
25029                 return this.doc.createRange();
25030             }
25031         } else {
25032             return this.doc.createRange();
25033         }
25034     },
25035     getParentElement: function()
25036     {
25037         
25038         this.assignDocWin();
25039         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25040         
25041         var range = this.createRange(sel);
25042          
25043         try {
25044             var p = range.commonAncestorContainer;
25045             while (p.nodeType == 3) { // text node
25046                 p = p.parentNode;
25047             }
25048             return p;
25049         } catch (e) {
25050             return null;
25051         }
25052     
25053     },
25054     /***
25055      *
25056      * Range intersection.. the hard stuff...
25057      *  '-1' = before
25058      *  '0' = hits..
25059      *  '1' = after.
25060      *         [ -- selected range --- ]
25061      *   [fail]                        [fail]
25062      *
25063      *    basically..
25064      *      if end is before start or  hits it. fail.
25065      *      if start is after end or hits it fail.
25066      *
25067      *   if either hits (but other is outside. - then it's not 
25068      *   
25069      *    
25070      **/
25071     
25072     
25073     // @see http://www.thismuchiknow.co.uk/?p=64.
25074     rangeIntersectsNode : function(range, node)
25075     {
25076         var nodeRange = node.ownerDocument.createRange();
25077         try {
25078             nodeRange.selectNode(node);
25079         } catch (e) {
25080             nodeRange.selectNodeContents(node);
25081         }
25082     
25083         var rangeStartRange = range.cloneRange();
25084         rangeStartRange.collapse(true);
25085     
25086         var rangeEndRange = range.cloneRange();
25087         rangeEndRange.collapse(false);
25088     
25089         var nodeStartRange = nodeRange.cloneRange();
25090         nodeStartRange.collapse(true);
25091     
25092         var nodeEndRange = nodeRange.cloneRange();
25093         nodeEndRange.collapse(false);
25094     
25095         return rangeStartRange.compareBoundaryPoints(
25096                  Range.START_TO_START, nodeEndRange) == -1 &&
25097                rangeEndRange.compareBoundaryPoints(
25098                  Range.START_TO_START, nodeStartRange) == 1;
25099         
25100          
25101     },
25102     rangeCompareNode : function(range, node)
25103     {
25104         var nodeRange = node.ownerDocument.createRange();
25105         try {
25106             nodeRange.selectNode(node);
25107         } catch (e) {
25108             nodeRange.selectNodeContents(node);
25109         }
25110         
25111         
25112         range.collapse(true);
25113     
25114         nodeRange.collapse(true);
25115      
25116         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25117         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25118          
25119         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25120         
25121         var nodeIsBefore   =  ss == 1;
25122         var nodeIsAfter    = ee == -1;
25123         
25124         if (nodeIsBefore && nodeIsAfter) {
25125             return 0; // outer
25126         }
25127         if (!nodeIsBefore && nodeIsAfter) {
25128             return 1; //right trailed.
25129         }
25130         
25131         if (nodeIsBefore && !nodeIsAfter) {
25132             return 2;  // left trailed.
25133         }
25134         // fully contined.
25135         return 3;
25136     },
25137
25138     // private? - in a new class?
25139     cleanUpPaste :  function()
25140     {
25141         // cleans up the whole document..
25142         Roo.log('cleanuppaste');
25143         
25144         this.cleanUpChildren(this.doc.body);
25145         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25146         if (clean != this.doc.body.innerHTML) {
25147             this.doc.body.innerHTML = clean;
25148         }
25149         
25150     },
25151     
25152     cleanWordChars : function(input) {// change the chars to hex code
25153         var he = Roo.HtmlEditorCore;
25154         
25155         var output = input;
25156         Roo.each(he.swapCodes, function(sw) { 
25157             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25158             
25159             output = output.replace(swapper, sw[1]);
25160         });
25161         
25162         return output;
25163     },
25164     
25165     
25166     cleanUpChildren : function (n)
25167     {
25168         if (!n.childNodes.length) {
25169             return;
25170         }
25171         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25172            this.cleanUpChild(n.childNodes[i]);
25173         }
25174     },
25175     
25176     
25177         
25178     
25179     cleanUpChild : function (node)
25180     {
25181         var ed = this;
25182         //console.log(node);
25183         if (node.nodeName == "#text") {
25184             // clean up silly Windows -- stuff?
25185             return; 
25186         }
25187         if (node.nodeName == "#comment") {
25188             node.parentNode.removeChild(node);
25189             // clean up silly Windows -- stuff?
25190             return; 
25191         }
25192         var lcname = node.tagName.toLowerCase();
25193         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25194         // whitelist of tags..
25195         
25196         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25197             // remove node.
25198             node.parentNode.removeChild(node);
25199             return;
25200             
25201         }
25202         
25203         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25204         
25205         // spans with no attributes - just remove them..
25206         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25207             remove_keep_children = true;
25208         }
25209         
25210         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25211         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25212         
25213         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25214         //    remove_keep_children = true;
25215         //}
25216         
25217         if (remove_keep_children) {
25218             this.cleanUpChildren(node);
25219             // inserts everything just before this node...
25220             while (node.childNodes.length) {
25221                 var cn = node.childNodes[0];
25222                 node.removeChild(cn);
25223                 node.parentNode.insertBefore(cn, node);
25224             }
25225             node.parentNode.removeChild(node);
25226             return;
25227         }
25228         
25229         if (!node.attributes || !node.attributes.length) {
25230             
25231           
25232             
25233             
25234             this.cleanUpChildren(node);
25235             return;
25236         }
25237         
25238         function cleanAttr(n,v)
25239         {
25240             
25241             if (v.match(/^\./) || v.match(/^\//)) {
25242                 return;
25243             }
25244             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25245                 return;
25246             }
25247             if (v.match(/^#/)) {
25248                 return;
25249             }
25250             if (v.match(/^\{/)) { // allow template editing.
25251                 return;
25252             }
25253 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25254             node.removeAttribute(n);
25255             
25256         }
25257         
25258         var cwhite = this.cwhite;
25259         var cblack = this.cblack;
25260             
25261         function cleanStyle(n,v)
25262         {
25263             if (v.match(/expression/)) { //XSS?? should we even bother..
25264                 node.removeAttribute(n);
25265                 return;
25266             }
25267             
25268             var parts = v.split(/;/);
25269             var clean = [];
25270             
25271             Roo.each(parts, function(p) {
25272                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25273                 if (!p.length) {
25274                     return true;
25275                 }
25276                 var l = p.split(':').shift().replace(/\s+/g,'');
25277                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25278                 
25279                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25280 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25281                     //node.removeAttribute(n);
25282                     return true;
25283                 }
25284                 //Roo.log()
25285                 // only allow 'c whitelisted system attributes'
25286                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25287 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25288                     //node.removeAttribute(n);
25289                     return true;
25290                 }
25291                 
25292                 
25293                  
25294                 
25295                 clean.push(p);
25296                 return true;
25297             });
25298             if (clean.length) { 
25299                 node.setAttribute(n, clean.join(';'));
25300             } else {
25301                 node.removeAttribute(n);
25302             }
25303             
25304         }
25305         
25306         
25307         for (var i = node.attributes.length-1; i > -1 ; i--) {
25308             var a = node.attributes[i];
25309             //console.log(a);
25310             
25311             if (a.name.toLowerCase().substr(0,2)=='on')  {
25312                 node.removeAttribute(a.name);
25313                 continue;
25314             }
25315             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25316                 node.removeAttribute(a.name);
25317                 continue;
25318             }
25319             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25320                 cleanAttr(a.name,a.value); // fixme..
25321                 continue;
25322             }
25323             if (a.name == 'style') {
25324                 cleanStyle(a.name,a.value);
25325                 continue;
25326             }
25327             /// clean up MS crap..
25328             // tecnically this should be a list of valid class'es..
25329             
25330             
25331             if (a.name == 'class') {
25332                 if (a.value.match(/^Mso/)) {
25333                     node.removeAttribute('class');
25334                 }
25335                 
25336                 if (a.value.match(/^body$/)) {
25337                     node.removeAttribute('class');
25338                 }
25339                 continue;
25340             }
25341             
25342             // style cleanup!?
25343             // class cleanup?
25344             
25345         }
25346         
25347         
25348         this.cleanUpChildren(node);
25349         
25350         
25351     },
25352     
25353     /**
25354      * Clean up MS wordisms...
25355      */
25356     cleanWord : function(node)
25357     {
25358         if (!node) {
25359             this.cleanWord(this.doc.body);
25360             return;
25361         }
25362         
25363         if(
25364                 node.nodeName == 'SPAN' &&
25365                 !node.hasAttributes() &&
25366                 node.childNodes.length == 1 &&
25367                 node.firstChild.nodeName == "#text"  
25368         ) {
25369             var textNode = node.firstChild;
25370             node.removeChild(textNode);
25371             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25372                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25373             }
25374             node.parentNode.insertBefore(textNode, node);
25375             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25376                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25377             }
25378             node.parentNode.removeChild(node);
25379         }
25380         
25381         if (node.nodeName == "#text") {
25382             // clean up silly Windows -- stuff?
25383             return; 
25384         }
25385         if (node.nodeName == "#comment") {
25386             node.parentNode.removeChild(node);
25387             // clean up silly Windows -- stuff?
25388             return; 
25389         }
25390         
25391         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25392             node.parentNode.removeChild(node);
25393             return;
25394         }
25395         //Roo.log(node.tagName);
25396         // remove - but keep children..
25397         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25398             //Roo.log('-- removed');
25399             while (node.childNodes.length) {
25400                 var cn = node.childNodes[0];
25401                 node.removeChild(cn);
25402                 node.parentNode.insertBefore(cn, node);
25403                 // move node to parent - and clean it..
25404                 this.cleanWord(cn);
25405             }
25406             node.parentNode.removeChild(node);
25407             /// no need to iterate chidlren = it's got none..
25408             //this.iterateChildren(node, this.cleanWord);
25409             return;
25410         }
25411         // clean styles
25412         if (node.className.length) {
25413             
25414             var cn = node.className.split(/\W+/);
25415             var cna = [];
25416             Roo.each(cn, function(cls) {
25417                 if (cls.match(/Mso[a-zA-Z]+/)) {
25418                     return;
25419                 }
25420                 cna.push(cls);
25421             });
25422             node.className = cna.length ? cna.join(' ') : '';
25423             if (!cna.length) {
25424                 node.removeAttribute("class");
25425             }
25426         }
25427         
25428         if (node.hasAttribute("lang")) {
25429             node.removeAttribute("lang");
25430         }
25431         
25432         if (node.hasAttribute("style")) {
25433             
25434             var styles = node.getAttribute("style").split(";");
25435             var nstyle = [];
25436             Roo.each(styles, function(s) {
25437                 if (!s.match(/:/)) {
25438                     return;
25439                 }
25440                 var kv = s.split(":");
25441                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25442                     return;
25443                 }
25444                 // what ever is left... we allow.
25445                 nstyle.push(s);
25446             });
25447             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25448             if (!nstyle.length) {
25449                 node.removeAttribute('style');
25450             }
25451         }
25452         this.iterateChildren(node, this.cleanWord);
25453         
25454         
25455         
25456     },
25457     /**
25458      * iterateChildren of a Node, calling fn each time, using this as the scole..
25459      * @param {DomNode} node node to iterate children of.
25460      * @param {Function} fn method of this class to call on each item.
25461      */
25462     iterateChildren : function(node, fn)
25463     {
25464         if (!node.childNodes.length) {
25465                 return;
25466         }
25467         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25468            fn.call(this, node.childNodes[i])
25469         }
25470     },
25471     
25472     
25473     /**
25474      * cleanTableWidths.
25475      *
25476      * Quite often pasting from word etc.. results in tables with column and widths.
25477      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25478      *
25479      */
25480     cleanTableWidths : function(node)
25481     {
25482          
25483          
25484         if (!node) {
25485             this.cleanTableWidths(this.doc.body);
25486             return;
25487         }
25488         
25489         // ignore list...
25490         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25491             return; 
25492         }
25493         Roo.log(node.tagName);
25494         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25495             this.iterateChildren(node, this.cleanTableWidths);
25496             return;
25497         }
25498         if (node.hasAttribute('width')) {
25499             node.removeAttribute('width');
25500         }
25501         
25502          
25503         if (node.hasAttribute("style")) {
25504             // pretty basic...
25505             
25506             var styles = node.getAttribute("style").split(";");
25507             var nstyle = [];
25508             Roo.each(styles, function(s) {
25509                 if (!s.match(/:/)) {
25510                     return;
25511                 }
25512                 var kv = s.split(":");
25513                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25514                     return;
25515                 }
25516                 // what ever is left... we allow.
25517                 nstyle.push(s);
25518             });
25519             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25520             if (!nstyle.length) {
25521                 node.removeAttribute('style');
25522             }
25523         }
25524         
25525         this.iterateChildren(node, this.cleanTableWidths);
25526         
25527         
25528     },
25529     
25530     
25531     
25532     
25533     domToHTML : function(currentElement, depth, nopadtext) {
25534         
25535         depth = depth || 0;
25536         nopadtext = nopadtext || false;
25537     
25538         if (!currentElement) {
25539             return this.domToHTML(this.doc.body);
25540         }
25541         
25542         //Roo.log(currentElement);
25543         var j;
25544         var allText = false;
25545         var nodeName = currentElement.nodeName;
25546         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25547         
25548         if  (nodeName == '#text') {
25549             
25550             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25551         }
25552         
25553         
25554         var ret = '';
25555         if (nodeName != 'BODY') {
25556              
25557             var i = 0;
25558             // Prints the node tagName, such as <A>, <IMG>, etc
25559             if (tagName) {
25560                 var attr = [];
25561                 for(i = 0; i < currentElement.attributes.length;i++) {
25562                     // quoting?
25563                     var aname = currentElement.attributes.item(i).name;
25564                     if (!currentElement.attributes.item(i).value.length) {
25565                         continue;
25566                     }
25567                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25568                 }
25569                 
25570                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25571             } 
25572             else {
25573                 
25574                 // eack
25575             }
25576         } else {
25577             tagName = false;
25578         }
25579         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25580             return ret;
25581         }
25582         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25583             nopadtext = true;
25584         }
25585         
25586         
25587         // Traverse the tree
25588         i = 0;
25589         var currentElementChild = currentElement.childNodes.item(i);
25590         var allText = true;
25591         var innerHTML  = '';
25592         lastnode = '';
25593         while (currentElementChild) {
25594             // Formatting code (indent the tree so it looks nice on the screen)
25595             var nopad = nopadtext;
25596             if (lastnode == 'SPAN') {
25597                 nopad  = true;
25598             }
25599             // text
25600             if  (currentElementChild.nodeName == '#text') {
25601                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25602                 toadd = nopadtext ? toadd : toadd.trim();
25603                 if (!nopad && toadd.length > 80) {
25604                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25605                 }
25606                 innerHTML  += toadd;
25607                 
25608                 i++;
25609                 currentElementChild = currentElement.childNodes.item(i);
25610                 lastNode = '';
25611                 continue;
25612             }
25613             allText = false;
25614             
25615             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25616                 
25617             // Recursively traverse the tree structure of the child node
25618             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25619             lastnode = currentElementChild.nodeName;
25620             i++;
25621             currentElementChild=currentElement.childNodes.item(i);
25622         }
25623         
25624         ret += innerHTML;
25625         
25626         if (!allText) {
25627                 // The remaining code is mostly for formatting the tree
25628             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25629         }
25630         
25631         
25632         if (tagName) {
25633             ret+= "</"+tagName+">";
25634         }
25635         return ret;
25636         
25637     },
25638         
25639     applyBlacklists : function()
25640     {
25641         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25642         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25643         
25644         this.white = [];
25645         this.black = [];
25646         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25647             if (b.indexOf(tag) > -1) {
25648                 return;
25649             }
25650             this.white.push(tag);
25651             
25652         }, this);
25653         
25654         Roo.each(w, function(tag) {
25655             if (b.indexOf(tag) > -1) {
25656                 return;
25657             }
25658             if (this.white.indexOf(tag) > -1) {
25659                 return;
25660             }
25661             this.white.push(tag);
25662             
25663         }, this);
25664         
25665         
25666         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25667             if (w.indexOf(tag) > -1) {
25668                 return;
25669             }
25670             this.black.push(tag);
25671             
25672         }, this);
25673         
25674         Roo.each(b, function(tag) {
25675             if (w.indexOf(tag) > -1) {
25676                 return;
25677             }
25678             if (this.black.indexOf(tag) > -1) {
25679                 return;
25680             }
25681             this.black.push(tag);
25682             
25683         }, this);
25684         
25685         
25686         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25687         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25688         
25689         this.cwhite = [];
25690         this.cblack = [];
25691         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25692             if (b.indexOf(tag) > -1) {
25693                 return;
25694             }
25695             this.cwhite.push(tag);
25696             
25697         }, this);
25698         
25699         Roo.each(w, function(tag) {
25700             if (b.indexOf(tag) > -1) {
25701                 return;
25702             }
25703             if (this.cwhite.indexOf(tag) > -1) {
25704                 return;
25705             }
25706             this.cwhite.push(tag);
25707             
25708         }, this);
25709         
25710         
25711         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25712             if (w.indexOf(tag) > -1) {
25713                 return;
25714             }
25715             this.cblack.push(tag);
25716             
25717         }, this);
25718         
25719         Roo.each(b, function(tag) {
25720             if (w.indexOf(tag) > -1) {
25721                 return;
25722             }
25723             if (this.cblack.indexOf(tag) > -1) {
25724                 return;
25725             }
25726             this.cblack.push(tag);
25727             
25728         }, this);
25729     },
25730     
25731     setStylesheets : function(stylesheets)
25732     {
25733         if(typeof(stylesheets) == 'string'){
25734             Roo.get(this.iframe.contentDocument.head).createChild({
25735                 tag : 'link',
25736                 rel : 'stylesheet',
25737                 type : 'text/css',
25738                 href : stylesheets
25739             });
25740             
25741             return;
25742         }
25743         var _this = this;
25744      
25745         Roo.each(stylesheets, function(s) {
25746             if(!s.length){
25747                 return;
25748             }
25749             
25750             Roo.get(_this.iframe.contentDocument.head).createChild({
25751                 tag : 'link',
25752                 rel : 'stylesheet',
25753                 type : 'text/css',
25754                 href : s
25755             });
25756         });
25757
25758         
25759     },
25760     
25761     removeStylesheets : function()
25762     {
25763         var _this = this;
25764         
25765         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25766             s.remove();
25767         });
25768     },
25769     
25770     setStyle : function(style)
25771     {
25772         Roo.get(this.iframe.contentDocument.head).createChild({
25773             tag : 'style',
25774             type : 'text/css',
25775             html : style
25776         });
25777
25778         return;
25779     }
25780     
25781     // hide stuff that is not compatible
25782     /**
25783      * @event blur
25784      * @hide
25785      */
25786     /**
25787      * @event change
25788      * @hide
25789      */
25790     /**
25791      * @event focus
25792      * @hide
25793      */
25794     /**
25795      * @event specialkey
25796      * @hide
25797      */
25798     /**
25799      * @cfg {String} fieldClass @hide
25800      */
25801     /**
25802      * @cfg {String} focusClass @hide
25803      */
25804     /**
25805      * @cfg {String} autoCreate @hide
25806      */
25807     /**
25808      * @cfg {String} inputType @hide
25809      */
25810     /**
25811      * @cfg {String} invalidClass @hide
25812      */
25813     /**
25814      * @cfg {String} invalidText @hide
25815      */
25816     /**
25817      * @cfg {String} msgFx @hide
25818      */
25819     /**
25820      * @cfg {String} validateOnBlur @hide
25821      */
25822 });
25823
25824 Roo.HtmlEditorCore.white = [
25825         'area', 'br', 'img', 'input', 'hr', 'wbr',
25826         
25827        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25828        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25829        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25830        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25831        'table',   'ul',         'xmp', 
25832        
25833        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25834       'thead',   'tr', 
25835      
25836       'dir', 'menu', 'ol', 'ul', 'dl',
25837        
25838       'embed',  'object'
25839 ];
25840
25841
25842 Roo.HtmlEditorCore.black = [
25843     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25844         'applet', // 
25845         'base',   'basefont', 'bgsound', 'blink',  'body', 
25846         'frame',  'frameset', 'head',    'html',   'ilayer', 
25847         'iframe', 'layer',  'link',     'meta',    'object',   
25848         'script', 'style' ,'title',  'xml' // clean later..
25849 ];
25850 Roo.HtmlEditorCore.clean = [
25851     'script', 'style', 'title', 'xml'
25852 ];
25853 Roo.HtmlEditorCore.remove = [
25854     'font'
25855 ];
25856 // attributes..
25857
25858 Roo.HtmlEditorCore.ablack = [
25859     'on'
25860 ];
25861     
25862 Roo.HtmlEditorCore.aclean = [ 
25863     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25864 ];
25865
25866 // protocols..
25867 Roo.HtmlEditorCore.pwhite= [
25868         'http',  'https',  'mailto'
25869 ];
25870
25871 // white listed style attributes.
25872 Roo.HtmlEditorCore.cwhite= [
25873       //  'text-align', /// default is to allow most things..
25874       
25875          
25876 //        'font-size'//??
25877 ];
25878
25879 // black listed style attributes.
25880 Roo.HtmlEditorCore.cblack= [
25881       //  'font-size' -- this can be set by the project 
25882 ];
25883
25884
25885 Roo.HtmlEditorCore.swapCodes   =[ 
25886     [    8211, "&#8211;" ], 
25887     [    8212, "&#8212;" ], 
25888     [    8216,  "'" ],  
25889     [    8217, "'" ],  
25890     [    8220, '"' ],  
25891     [    8221, '"' ],  
25892     [    8226, "*" ],  
25893     [    8230, "..." ]
25894 ]; 
25895
25896     /*
25897  * - LGPL
25898  *
25899  * HtmlEditor
25900  * 
25901  */
25902
25903 /**
25904  * @class Roo.bootstrap.HtmlEditor
25905  * @extends Roo.bootstrap.TextArea
25906  * Bootstrap HtmlEditor class
25907
25908  * @constructor
25909  * Create a new HtmlEditor
25910  * @param {Object} config The config object
25911  */
25912
25913 Roo.bootstrap.HtmlEditor = function(config){
25914     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25915     if (!this.toolbars) {
25916         this.toolbars = [];
25917     }
25918     
25919     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25920     this.addEvents({
25921             /**
25922              * @event initialize
25923              * Fires when the editor is fully initialized (including the iframe)
25924              * @param {HtmlEditor} this
25925              */
25926             initialize: true,
25927             /**
25928              * @event activate
25929              * Fires when the editor is first receives the focus. Any insertion must wait
25930              * until after this event.
25931              * @param {HtmlEditor} this
25932              */
25933             activate: true,
25934              /**
25935              * @event beforesync
25936              * Fires before the textarea is updated with content from the editor iframe. Return false
25937              * to cancel the sync.
25938              * @param {HtmlEditor} this
25939              * @param {String} html
25940              */
25941             beforesync: true,
25942              /**
25943              * @event beforepush
25944              * Fires before the iframe editor is updated with content from the textarea. Return false
25945              * to cancel the push.
25946              * @param {HtmlEditor} this
25947              * @param {String} html
25948              */
25949             beforepush: true,
25950              /**
25951              * @event sync
25952              * Fires when the textarea is updated with content from the editor iframe.
25953              * @param {HtmlEditor} this
25954              * @param {String} html
25955              */
25956             sync: true,
25957              /**
25958              * @event push
25959              * Fires when the iframe editor is updated with content from the textarea.
25960              * @param {HtmlEditor} this
25961              * @param {String} html
25962              */
25963             push: true,
25964              /**
25965              * @event editmodechange
25966              * Fires when the editor switches edit modes
25967              * @param {HtmlEditor} this
25968              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25969              */
25970             editmodechange: true,
25971             /**
25972              * @event editorevent
25973              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25974              * @param {HtmlEditor} this
25975              */
25976             editorevent: true,
25977             /**
25978              * @event firstfocus
25979              * Fires when on first focus - needed by toolbars..
25980              * @param {HtmlEditor} this
25981              */
25982             firstfocus: true,
25983             /**
25984              * @event autosave
25985              * Auto save the htmlEditor value as a file into Events
25986              * @param {HtmlEditor} this
25987              */
25988             autosave: true,
25989             /**
25990              * @event savedpreview
25991              * preview the saved version of htmlEditor
25992              * @param {HtmlEditor} this
25993              */
25994             savedpreview: true
25995         });
25996 };
25997
25998
25999 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26000     
26001     
26002       /**
26003      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26004      */
26005     toolbars : false,
26006     
26007      /**
26008     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26009     */
26010     btns : [],
26011    
26012      /**
26013      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26014      *                        Roo.resizable.
26015      */
26016     resizable : false,
26017      /**
26018      * @cfg {Number} height (in pixels)
26019      */   
26020     height: 300,
26021    /**
26022      * @cfg {Number} width (in pixels)
26023      */   
26024     width: false,
26025     
26026     /**
26027      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26028      * 
26029      */
26030     stylesheets: false,
26031     
26032     // id of frame..
26033     frameId: false,
26034     
26035     // private properties
26036     validationEvent : false,
26037     deferHeight: true,
26038     initialized : false,
26039     activated : false,
26040     
26041     onFocus : Roo.emptyFn,
26042     iframePad:3,
26043     hideMode:'offsets',
26044     
26045     tbContainer : false,
26046     
26047     bodyCls : '',
26048     
26049     toolbarContainer :function() {
26050         return this.wrap.select('.x-html-editor-tb',true).first();
26051     },
26052
26053     /**
26054      * Protected method that will not generally be called directly. It
26055      * is called when the editor creates its toolbar. Override this method if you need to
26056      * add custom toolbar buttons.
26057      * @param {HtmlEditor} editor
26058      */
26059     createToolbar : function(){
26060         Roo.log('renewing');
26061         Roo.log("create toolbars");
26062         
26063         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26064         this.toolbars[0].render(this.toolbarContainer());
26065         
26066         return;
26067         
26068 //        if (!editor.toolbars || !editor.toolbars.length) {
26069 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26070 //        }
26071 //        
26072 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26073 //            editor.toolbars[i] = Roo.factory(
26074 //                    typeof(editor.toolbars[i]) == 'string' ?
26075 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26076 //                Roo.bootstrap.HtmlEditor);
26077 //            editor.toolbars[i].init(editor);
26078 //        }
26079     },
26080
26081      
26082     // private
26083     onRender : function(ct, position)
26084     {
26085        // Roo.log("Call onRender: " + this.xtype);
26086         var _t = this;
26087         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26088       
26089         this.wrap = this.inputEl().wrap({
26090             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26091         });
26092         
26093         this.editorcore.onRender(ct, position);
26094          
26095         if (this.resizable) {
26096             this.resizeEl = new Roo.Resizable(this.wrap, {
26097                 pinned : true,
26098                 wrap: true,
26099                 dynamic : true,
26100                 minHeight : this.height,
26101                 height: this.height,
26102                 handles : this.resizable,
26103                 width: this.width,
26104                 listeners : {
26105                     resize : function(r, w, h) {
26106                         _t.onResize(w,h); // -something
26107                     }
26108                 }
26109             });
26110             
26111         }
26112         this.createToolbar(this);
26113        
26114         
26115         if(!this.width && this.resizable){
26116             this.setSize(this.wrap.getSize());
26117         }
26118         if (this.resizeEl) {
26119             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26120             // should trigger onReize..
26121         }
26122         
26123     },
26124
26125     // private
26126     onResize : function(w, h)
26127     {
26128         Roo.log('resize: ' +w + ',' + h );
26129         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26130         var ew = false;
26131         var eh = false;
26132         
26133         if(this.inputEl() ){
26134             if(typeof w == 'number'){
26135                 var aw = w - this.wrap.getFrameWidth('lr');
26136                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26137                 ew = aw;
26138             }
26139             if(typeof h == 'number'){
26140                  var tbh = -11;  // fixme it needs to tool bar size!
26141                 for (var i =0; i < this.toolbars.length;i++) {
26142                     // fixme - ask toolbars for heights?
26143                     tbh += this.toolbars[i].el.getHeight();
26144                     //if (this.toolbars[i].footer) {
26145                     //    tbh += this.toolbars[i].footer.el.getHeight();
26146                     //}
26147                 }
26148               
26149                 
26150                 
26151                 
26152                 
26153                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26154                 ah -= 5; // knock a few pixes off for look..
26155                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26156                 var eh = ah;
26157             }
26158         }
26159         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26160         this.editorcore.onResize(ew,eh);
26161         
26162     },
26163
26164     /**
26165      * Toggles the editor between standard and source edit mode.
26166      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26167      */
26168     toggleSourceEdit : function(sourceEditMode)
26169     {
26170         this.editorcore.toggleSourceEdit(sourceEditMode);
26171         
26172         if(this.editorcore.sourceEditMode){
26173             Roo.log('editor - showing textarea');
26174             
26175 //            Roo.log('in');
26176 //            Roo.log(this.syncValue());
26177             this.syncValue();
26178             this.inputEl().removeClass(['hide', 'x-hidden']);
26179             this.inputEl().dom.removeAttribute('tabIndex');
26180             this.inputEl().focus();
26181         }else{
26182             Roo.log('editor - hiding textarea');
26183 //            Roo.log('out')
26184 //            Roo.log(this.pushValue()); 
26185             this.pushValue();
26186             
26187             this.inputEl().addClass(['hide', 'x-hidden']);
26188             this.inputEl().dom.setAttribute('tabIndex', -1);
26189             //this.deferFocus();
26190         }
26191          
26192         if(this.resizable){
26193             this.setSize(this.wrap.getSize());
26194         }
26195         
26196         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26197     },
26198  
26199     // private (for BoxComponent)
26200     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26201
26202     // private (for BoxComponent)
26203     getResizeEl : function(){
26204         return this.wrap;
26205     },
26206
26207     // private (for BoxComponent)
26208     getPositionEl : function(){
26209         return this.wrap;
26210     },
26211
26212     // private
26213     initEvents : function(){
26214         this.originalValue = this.getValue();
26215     },
26216
26217 //    /**
26218 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26219 //     * @method
26220 //     */
26221 //    markInvalid : Roo.emptyFn,
26222 //    /**
26223 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26224 //     * @method
26225 //     */
26226 //    clearInvalid : Roo.emptyFn,
26227
26228     setValue : function(v){
26229         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26230         this.editorcore.pushValue();
26231     },
26232
26233      
26234     // private
26235     deferFocus : function(){
26236         this.focus.defer(10, this);
26237     },
26238
26239     // doc'ed in Field
26240     focus : function(){
26241         this.editorcore.focus();
26242         
26243     },
26244       
26245
26246     // private
26247     onDestroy : function(){
26248         
26249         
26250         
26251         if(this.rendered){
26252             
26253             for (var i =0; i < this.toolbars.length;i++) {
26254                 // fixme - ask toolbars for heights?
26255                 this.toolbars[i].onDestroy();
26256             }
26257             
26258             this.wrap.dom.innerHTML = '';
26259             this.wrap.remove();
26260         }
26261     },
26262
26263     // private
26264     onFirstFocus : function(){
26265         //Roo.log("onFirstFocus");
26266         this.editorcore.onFirstFocus();
26267          for (var i =0; i < this.toolbars.length;i++) {
26268             this.toolbars[i].onFirstFocus();
26269         }
26270         
26271     },
26272     
26273     // private
26274     syncValue : function()
26275     {   
26276         this.editorcore.syncValue();
26277     },
26278     
26279     pushValue : function()
26280     {   
26281         this.editorcore.pushValue();
26282     }
26283      
26284     
26285     // hide stuff that is not compatible
26286     /**
26287      * @event blur
26288      * @hide
26289      */
26290     /**
26291      * @event change
26292      * @hide
26293      */
26294     /**
26295      * @event focus
26296      * @hide
26297      */
26298     /**
26299      * @event specialkey
26300      * @hide
26301      */
26302     /**
26303      * @cfg {String} fieldClass @hide
26304      */
26305     /**
26306      * @cfg {String} focusClass @hide
26307      */
26308     /**
26309      * @cfg {String} autoCreate @hide
26310      */
26311     /**
26312      * @cfg {String} inputType @hide
26313      */
26314      
26315     /**
26316      * @cfg {String} invalidText @hide
26317      */
26318     /**
26319      * @cfg {String} msgFx @hide
26320      */
26321     /**
26322      * @cfg {String} validateOnBlur @hide
26323      */
26324 });
26325  
26326     
26327    
26328    
26329    
26330       
26331 Roo.namespace('Roo.bootstrap.htmleditor');
26332 /**
26333  * @class Roo.bootstrap.HtmlEditorToolbar1
26334  * Basic Toolbar
26335  * 
26336  * @example
26337  * Usage:
26338  *
26339  new Roo.bootstrap.HtmlEditor({
26340     ....
26341     toolbars : [
26342         new Roo.bootstrap.HtmlEditorToolbar1({
26343             disable : { fonts: 1 , format: 1, ..., ... , ...],
26344             btns : [ .... ]
26345         })
26346     }
26347      
26348  * 
26349  * @cfg {Object} disable List of elements to disable..
26350  * @cfg {Array} btns List of additional buttons.
26351  * 
26352  * 
26353  * NEEDS Extra CSS? 
26354  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26355  */
26356  
26357 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26358 {
26359     
26360     Roo.apply(this, config);
26361     
26362     // default disabled, based on 'good practice'..
26363     this.disable = this.disable || {};
26364     Roo.applyIf(this.disable, {
26365         fontSize : true,
26366         colors : true,
26367         specialElements : true
26368     });
26369     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26370     
26371     this.editor = config.editor;
26372     this.editorcore = config.editor.editorcore;
26373     
26374     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26375     
26376     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26377     // dont call parent... till later.
26378 }
26379 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26380      
26381     bar : true,
26382     
26383     editor : false,
26384     editorcore : false,
26385     
26386     
26387     formats : [
26388         "p" ,  
26389         "h1","h2","h3","h4","h5","h6", 
26390         "pre", "code", 
26391         "abbr", "acronym", "address", "cite", "samp", "var",
26392         'div','span'
26393     ],
26394     
26395     onRender : function(ct, position)
26396     {
26397        // Roo.log("Call onRender: " + this.xtype);
26398         
26399        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26400        Roo.log(this.el);
26401        this.el.dom.style.marginBottom = '0';
26402        var _this = this;
26403        var editorcore = this.editorcore;
26404        var editor= this.editor;
26405        
26406        var children = [];
26407        var btn = function(id,cmd , toggle, handler, html){
26408        
26409             var  event = toggle ? 'toggle' : 'click';
26410        
26411             var a = {
26412                 size : 'sm',
26413                 xtype: 'Button',
26414                 xns: Roo.bootstrap,
26415                 //glyphicon : id,
26416                 fa: id,
26417                 cmd : id || cmd,
26418                 enableToggle:toggle !== false,
26419                 html : html || '',
26420                 pressed : toggle ? false : null,
26421                 listeners : {}
26422             };
26423             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26424                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26425             };
26426             children.push(a);
26427             return a;
26428        }
26429        
26430     //    var cb_box = function...
26431         
26432         var style = {
26433                 xtype: 'Button',
26434                 size : 'sm',
26435                 xns: Roo.bootstrap,
26436                 fa : 'font',
26437                 //html : 'submit'
26438                 menu : {
26439                     xtype: 'Menu',
26440                     xns: Roo.bootstrap,
26441                     items:  []
26442                 }
26443         };
26444         Roo.each(this.formats, function(f) {
26445             style.menu.items.push({
26446                 xtype :'MenuItem',
26447                 xns: Roo.bootstrap,
26448                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26449                 tagname : f,
26450                 listeners : {
26451                     click : function()
26452                     {
26453                         editorcore.insertTag(this.tagname);
26454                         editor.focus();
26455                     }
26456                 }
26457                 
26458             });
26459         });
26460         children.push(style);   
26461         
26462         btn('bold',false,true);
26463         btn('italic',false,true);
26464         btn('align-left', 'justifyleft',true);
26465         btn('align-center', 'justifycenter',true);
26466         btn('align-right' , 'justifyright',true);
26467         btn('link', false, false, function(btn) {
26468             //Roo.log("create link?");
26469             var url = prompt(this.createLinkText, this.defaultLinkValue);
26470             if(url && url != 'http:/'+'/'){
26471                 this.editorcore.relayCmd('createlink', url);
26472             }
26473         }),
26474         btn('list','insertunorderedlist',true);
26475         btn('pencil', false,true, function(btn){
26476                 Roo.log(this);
26477                 this.toggleSourceEdit(btn.pressed);
26478         });
26479         
26480         if (this.editor.btns.length > 0) {
26481             for (var i = 0; i<this.editor.btns.length; i++) {
26482                 children.push(this.editor.btns[i]);
26483             }
26484         }
26485         
26486         /*
26487         var cog = {
26488                 xtype: 'Button',
26489                 size : 'sm',
26490                 xns: Roo.bootstrap,
26491                 glyphicon : 'cog',
26492                 //html : 'submit'
26493                 menu : {
26494                     xtype: 'Menu',
26495                     xns: Roo.bootstrap,
26496                     items:  []
26497                 }
26498         };
26499         
26500         cog.menu.items.push({
26501             xtype :'MenuItem',
26502             xns: Roo.bootstrap,
26503             html : Clean styles,
26504             tagname : f,
26505             listeners : {
26506                 click : function()
26507                 {
26508                     editorcore.insertTag(this.tagname);
26509                     editor.focus();
26510                 }
26511             }
26512             
26513         });
26514        */
26515         
26516          
26517        this.xtype = 'NavSimplebar';
26518         
26519         for(var i=0;i< children.length;i++) {
26520             
26521             this.buttons.add(this.addxtypeChild(children[i]));
26522             
26523         }
26524         
26525         editor.on('editorevent', this.updateToolbar, this);
26526     },
26527     onBtnClick : function(id)
26528     {
26529        this.editorcore.relayCmd(id);
26530        this.editorcore.focus();
26531     },
26532     
26533     /**
26534      * Protected method that will not generally be called directly. It triggers
26535      * a toolbar update by reading the markup state of the current selection in the editor.
26536      */
26537     updateToolbar: function(){
26538
26539         if(!this.editorcore.activated){
26540             this.editor.onFirstFocus(); // is this neeed?
26541             return;
26542         }
26543
26544         var btns = this.buttons; 
26545         var doc = this.editorcore.doc;
26546         btns.get('bold').setActive(doc.queryCommandState('bold'));
26547         btns.get('italic').setActive(doc.queryCommandState('italic'));
26548         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26549         
26550         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26551         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26552         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26553         
26554         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26555         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26556          /*
26557         
26558         var ans = this.editorcore.getAllAncestors();
26559         if (this.formatCombo) {
26560             
26561             
26562             var store = this.formatCombo.store;
26563             this.formatCombo.setValue("");
26564             for (var i =0; i < ans.length;i++) {
26565                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26566                     // select it..
26567                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26568                     break;
26569                 }
26570             }
26571         }
26572         
26573         
26574         
26575         // hides menus... - so this cant be on a menu...
26576         Roo.bootstrap.MenuMgr.hideAll();
26577         */
26578         Roo.bootstrap.MenuMgr.hideAll();
26579         //this.editorsyncValue();
26580     },
26581     onFirstFocus: function() {
26582         this.buttons.each(function(item){
26583            item.enable();
26584         });
26585     },
26586     toggleSourceEdit : function(sourceEditMode){
26587         
26588           
26589         if(sourceEditMode){
26590             Roo.log("disabling buttons");
26591            this.buttons.each( function(item){
26592                 if(item.cmd != 'pencil'){
26593                     item.disable();
26594                 }
26595             });
26596           
26597         }else{
26598             Roo.log("enabling buttons");
26599             if(this.editorcore.initialized){
26600                 this.buttons.each( function(item){
26601                     item.enable();
26602                 });
26603             }
26604             
26605         }
26606         Roo.log("calling toggole on editor");
26607         // tell the editor that it's been pressed..
26608         this.editor.toggleSourceEdit(sourceEditMode);
26609        
26610     }
26611 });
26612
26613
26614
26615
26616  
26617 /*
26618  * - LGPL
26619  */
26620
26621 /**
26622  * @class Roo.bootstrap.Markdown
26623  * @extends Roo.bootstrap.TextArea
26624  * Bootstrap Showdown editable area
26625  * @cfg {string} content
26626  * 
26627  * @constructor
26628  * Create a new Showdown
26629  */
26630
26631 Roo.bootstrap.Markdown = function(config){
26632     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26633    
26634 };
26635
26636 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26637     
26638     editing :false,
26639     
26640     initEvents : function()
26641     {
26642         
26643         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26644         this.markdownEl = this.el.createChild({
26645             cls : 'roo-markdown-area'
26646         });
26647         this.inputEl().addClass('d-none');
26648         if (this.getValue() == '') {
26649             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26650             
26651         } else {
26652             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26653         }
26654         this.markdownEl.on('click', this.toggleTextEdit, this);
26655         this.on('blur', this.toggleTextEdit, this);
26656         this.on('specialkey', this.resizeTextArea, this);
26657     },
26658     
26659     toggleTextEdit : function()
26660     {
26661         var sh = this.markdownEl.getHeight();
26662         this.inputEl().addClass('d-none');
26663         this.markdownEl.addClass('d-none');
26664         if (!this.editing) {
26665             // show editor?
26666             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26667             this.inputEl().removeClass('d-none');
26668             this.inputEl().focus();
26669             this.editing = true;
26670             return;
26671         }
26672         // show showdown...
26673         this.updateMarkdown();
26674         this.markdownEl.removeClass('d-none');
26675         this.editing = false;
26676         return;
26677     },
26678     updateMarkdown : function()
26679     {
26680         if (this.getValue() == '') {
26681             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26682             return;
26683         }
26684  
26685         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26686     },
26687     
26688     resizeTextArea: function () {
26689         
26690         var sh = 100;
26691         Roo.log([sh, this.getValue().split("\n").length * 30]);
26692         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26693     },
26694     setValue : function(val)
26695     {
26696         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26697         if (!this.editing) {
26698             this.updateMarkdown();
26699         }
26700         
26701     },
26702     focus : function()
26703     {
26704         if (!this.editing) {
26705             this.toggleTextEdit();
26706         }
26707         
26708     }
26709
26710
26711 });
26712 /**
26713  * @class Roo.bootstrap.Table.AbstractSelectionModel
26714  * @extends Roo.util.Observable
26715  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26716  * implemented by descendant classes.  This class should not be directly instantiated.
26717  * @constructor
26718  */
26719 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26720     this.locked = false;
26721     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26722 };
26723
26724
26725 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26726     /** @ignore Called by the grid automatically. Do not call directly. */
26727     init : function(grid){
26728         this.grid = grid;
26729         this.initEvents();
26730     },
26731
26732     /**
26733      * Locks the selections.
26734      */
26735     lock : function(){
26736         this.locked = true;
26737     },
26738
26739     /**
26740      * Unlocks the selections.
26741      */
26742     unlock : function(){
26743         this.locked = false;
26744     },
26745
26746     /**
26747      * Returns true if the selections are locked.
26748      * @return {Boolean}
26749      */
26750     isLocked : function(){
26751         return this.locked;
26752     },
26753     
26754     
26755     initEvents : function ()
26756     {
26757         
26758     }
26759 });
26760 /**
26761  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26762  * @class Roo.bootstrap.Table.RowSelectionModel
26763  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26764  * It supports multiple selections and keyboard selection/navigation. 
26765  * @constructor
26766  * @param {Object} config
26767  */
26768
26769 Roo.bootstrap.Table.RowSelectionModel = function(config){
26770     Roo.apply(this, config);
26771     this.selections = new Roo.util.MixedCollection(false, function(o){
26772         return o.id;
26773     });
26774
26775     this.last = false;
26776     this.lastActive = false;
26777
26778     this.addEvents({
26779         /**
26780              * @event selectionchange
26781              * Fires when the selection changes
26782              * @param {SelectionModel} this
26783              */
26784             "selectionchange" : true,
26785         /**
26786              * @event afterselectionchange
26787              * Fires after the selection changes (eg. by key press or clicking)
26788              * @param {SelectionModel} this
26789              */
26790             "afterselectionchange" : true,
26791         /**
26792              * @event beforerowselect
26793              * Fires when a row is selected being selected, return false to cancel.
26794              * @param {SelectionModel} this
26795              * @param {Number} rowIndex The selected index
26796              * @param {Boolean} keepExisting False if other selections will be cleared
26797              */
26798             "beforerowselect" : true,
26799         /**
26800              * @event rowselect
26801              * Fires when a row is selected.
26802              * @param {SelectionModel} this
26803              * @param {Number} rowIndex The selected index
26804              * @param {Roo.data.Record} r The record
26805              */
26806             "rowselect" : true,
26807         /**
26808              * @event rowdeselect
26809              * Fires when a row is deselected.
26810              * @param {SelectionModel} this
26811              * @param {Number} rowIndex The selected index
26812              */
26813         "rowdeselect" : true
26814     });
26815     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26816     this.locked = false;
26817  };
26818
26819 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26820     /**
26821      * @cfg {Boolean} singleSelect
26822      * True to allow selection of only one row at a time (defaults to false)
26823      */
26824     singleSelect : false,
26825
26826     // private
26827     initEvents : function()
26828     {
26829
26830         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26831         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26832         //}else{ // allow click to work like normal
26833          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26834         //}
26835         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26836         this.grid.on("rowclick", this.handleMouseDown, this);
26837         
26838         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26839             "up" : function(e){
26840                 if(!e.shiftKey){
26841                     this.selectPrevious(e.shiftKey);
26842                 }else if(this.last !== false && this.lastActive !== false){
26843                     var last = this.last;
26844                     this.selectRange(this.last,  this.lastActive-1);
26845                     this.grid.getView().focusRow(this.lastActive);
26846                     if(last !== false){
26847                         this.last = last;
26848                     }
26849                 }else{
26850                     this.selectFirstRow();
26851                 }
26852                 this.fireEvent("afterselectionchange", this);
26853             },
26854             "down" : function(e){
26855                 if(!e.shiftKey){
26856                     this.selectNext(e.shiftKey);
26857                 }else if(this.last !== false && this.lastActive !== false){
26858                     var last = this.last;
26859                     this.selectRange(this.last,  this.lastActive+1);
26860                     this.grid.getView().focusRow(this.lastActive);
26861                     if(last !== false){
26862                         this.last = last;
26863                     }
26864                 }else{
26865                     this.selectFirstRow();
26866                 }
26867                 this.fireEvent("afterselectionchange", this);
26868             },
26869             scope: this
26870         });
26871         this.grid.store.on('load', function(){
26872             this.selections.clear();
26873         },this);
26874         /*
26875         var view = this.grid.view;
26876         view.on("refresh", this.onRefresh, this);
26877         view.on("rowupdated", this.onRowUpdated, this);
26878         view.on("rowremoved", this.onRemove, this);
26879         */
26880     },
26881
26882     // private
26883     onRefresh : function()
26884     {
26885         var ds = this.grid.store, i, v = this.grid.view;
26886         var s = this.selections;
26887         s.each(function(r){
26888             if((i = ds.indexOfId(r.id)) != -1){
26889                 v.onRowSelect(i);
26890             }else{
26891                 s.remove(r);
26892             }
26893         });
26894     },
26895
26896     // private
26897     onRemove : function(v, index, r){
26898         this.selections.remove(r);
26899     },
26900
26901     // private
26902     onRowUpdated : function(v, index, r){
26903         if(this.isSelected(r)){
26904             v.onRowSelect(index);
26905         }
26906     },
26907
26908     /**
26909      * Select records.
26910      * @param {Array} records The records to select
26911      * @param {Boolean} keepExisting (optional) True to keep existing selections
26912      */
26913     selectRecords : function(records, keepExisting)
26914     {
26915         if(!keepExisting){
26916             this.clearSelections();
26917         }
26918             var ds = this.grid.store;
26919         for(var i = 0, len = records.length; i < len; i++){
26920             this.selectRow(ds.indexOf(records[i]), true);
26921         }
26922     },
26923
26924     /**
26925      * Gets the number of selected rows.
26926      * @return {Number}
26927      */
26928     getCount : function(){
26929         return this.selections.length;
26930     },
26931
26932     /**
26933      * Selects the first row in the grid.
26934      */
26935     selectFirstRow : function(){
26936         this.selectRow(0);
26937     },
26938
26939     /**
26940      * Select the last row.
26941      * @param {Boolean} keepExisting (optional) True to keep existing selections
26942      */
26943     selectLastRow : function(keepExisting){
26944         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26945         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26946     },
26947
26948     /**
26949      * Selects the row immediately following the last selected row.
26950      * @param {Boolean} keepExisting (optional) True to keep existing selections
26951      */
26952     selectNext : function(keepExisting)
26953     {
26954             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26955             this.selectRow(this.last+1, keepExisting);
26956             this.grid.getView().focusRow(this.last);
26957         }
26958     },
26959
26960     /**
26961      * Selects the row that precedes the last selected row.
26962      * @param {Boolean} keepExisting (optional) True to keep existing selections
26963      */
26964     selectPrevious : function(keepExisting){
26965         if(this.last){
26966             this.selectRow(this.last-1, keepExisting);
26967             this.grid.getView().focusRow(this.last);
26968         }
26969     },
26970
26971     /**
26972      * Returns the selected records
26973      * @return {Array} Array of selected records
26974      */
26975     getSelections : function(){
26976         return [].concat(this.selections.items);
26977     },
26978
26979     /**
26980      * Returns the first selected record.
26981      * @return {Record}
26982      */
26983     getSelected : function(){
26984         return this.selections.itemAt(0);
26985     },
26986
26987
26988     /**
26989      * Clears all selections.
26990      */
26991     clearSelections : function(fast)
26992     {
26993         if(this.locked) {
26994             return;
26995         }
26996         if(fast !== true){
26997                 var ds = this.grid.store;
26998             var s = this.selections;
26999             s.each(function(r){
27000                 this.deselectRow(ds.indexOfId(r.id));
27001             }, this);
27002             s.clear();
27003         }else{
27004             this.selections.clear();
27005         }
27006         this.last = false;
27007     },
27008
27009
27010     /**
27011      * Selects all rows.
27012      */
27013     selectAll : function(){
27014         if(this.locked) {
27015             return;
27016         }
27017         this.selections.clear();
27018         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27019             this.selectRow(i, true);
27020         }
27021     },
27022
27023     /**
27024      * Returns True if there is a selection.
27025      * @return {Boolean}
27026      */
27027     hasSelection : function(){
27028         return this.selections.length > 0;
27029     },
27030
27031     /**
27032      * Returns True if the specified row is selected.
27033      * @param {Number/Record} record The record or index of the record to check
27034      * @return {Boolean}
27035      */
27036     isSelected : function(index){
27037             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27038         return (r && this.selections.key(r.id) ? true : false);
27039     },
27040
27041     /**
27042      * Returns True if the specified record id is selected.
27043      * @param {String} id The id of record to check
27044      * @return {Boolean}
27045      */
27046     isIdSelected : function(id){
27047         return (this.selections.key(id) ? true : false);
27048     },
27049
27050
27051     // private
27052     handleMouseDBClick : function(e, t){
27053         
27054     },
27055     // private
27056     handleMouseDown : function(e, t)
27057     {
27058             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27059         if(this.isLocked() || rowIndex < 0 ){
27060             return;
27061         };
27062         if(e.shiftKey && this.last !== false){
27063             var last = this.last;
27064             this.selectRange(last, rowIndex, e.ctrlKey);
27065             this.last = last; // reset the last
27066             t.focus();
27067     
27068         }else{
27069             var isSelected = this.isSelected(rowIndex);
27070             //Roo.log("select row:" + rowIndex);
27071             if(isSelected){
27072                 this.deselectRow(rowIndex);
27073             } else {
27074                         this.selectRow(rowIndex, true);
27075             }
27076     
27077             /*
27078                 if(e.button !== 0 && isSelected){
27079                 alert('rowIndex 2: ' + rowIndex);
27080                     view.focusRow(rowIndex);
27081                 }else if(e.ctrlKey && isSelected){
27082                     this.deselectRow(rowIndex);
27083                 }else if(!isSelected){
27084                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27085                     view.focusRow(rowIndex);
27086                 }
27087             */
27088         }
27089         this.fireEvent("afterselectionchange", this);
27090     },
27091     // private
27092     handleDragableRowClick :  function(grid, rowIndex, e) 
27093     {
27094         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27095             this.selectRow(rowIndex, false);
27096             grid.view.focusRow(rowIndex);
27097              this.fireEvent("afterselectionchange", this);
27098         }
27099     },
27100     
27101     /**
27102      * Selects multiple rows.
27103      * @param {Array} rows Array of the indexes of the row to select
27104      * @param {Boolean} keepExisting (optional) True to keep existing selections
27105      */
27106     selectRows : function(rows, keepExisting){
27107         if(!keepExisting){
27108             this.clearSelections();
27109         }
27110         for(var i = 0, len = rows.length; i < len; i++){
27111             this.selectRow(rows[i], true);
27112         }
27113     },
27114
27115     /**
27116      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27117      * @param {Number} startRow The index of the first row in the range
27118      * @param {Number} endRow The index of the last row in the range
27119      * @param {Boolean} keepExisting (optional) True to retain existing selections
27120      */
27121     selectRange : function(startRow, endRow, keepExisting){
27122         if(this.locked) {
27123             return;
27124         }
27125         if(!keepExisting){
27126             this.clearSelections();
27127         }
27128         if(startRow <= endRow){
27129             for(var i = startRow; i <= endRow; i++){
27130                 this.selectRow(i, true);
27131             }
27132         }else{
27133             for(var i = startRow; i >= endRow; i--){
27134                 this.selectRow(i, true);
27135             }
27136         }
27137     },
27138
27139     /**
27140      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27141      * @param {Number} startRow The index of the first row in the range
27142      * @param {Number} endRow The index of the last row in the range
27143      */
27144     deselectRange : function(startRow, endRow, preventViewNotify){
27145         if(this.locked) {
27146             return;
27147         }
27148         for(var i = startRow; i <= endRow; i++){
27149             this.deselectRow(i, preventViewNotify);
27150         }
27151     },
27152
27153     /**
27154      * Selects a row.
27155      * @param {Number} row The index of the row to select
27156      * @param {Boolean} keepExisting (optional) True to keep existing selections
27157      */
27158     selectRow : function(index, keepExisting, preventViewNotify)
27159     {
27160             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27161             return;
27162         }
27163         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27164             if(!keepExisting || this.singleSelect){
27165                 this.clearSelections();
27166             }
27167             
27168             var r = this.grid.store.getAt(index);
27169             //console.log('selectRow - record id :' + r.id);
27170             
27171             this.selections.add(r);
27172             this.last = this.lastActive = index;
27173             if(!preventViewNotify){
27174                 var proxy = new Roo.Element(
27175                                 this.grid.getRowDom(index)
27176                 );
27177                 proxy.addClass('bg-info info');
27178             }
27179             this.fireEvent("rowselect", this, index, r);
27180             this.fireEvent("selectionchange", this);
27181         }
27182     },
27183
27184     /**
27185      * Deselects a row.
27186      * @param {Number} row The index of the row to deselect
27187      */
27188     deselectRow : function(index, preventViewNotify)
27189     {
27190         if(this.locked) {
27191             return;
27192         }
27193         if(this.last == index){
27194             this.last = false;
27195         }
27196         if(this.lastActive == index){
27197             this.lastActive = false;
27198         }
27199         
27200         var r = this.grid.store.getAt(index);
27201         if (!r) {
27202             return;
27203         }
27204         
27205         this.selections.remove(r);
27206         //.console.log('deselectRow - record id :' + r.id);
27207         if(!preventViewNotify){
27208         
27209             var proxy = new Roo.Element(
27210                 this.grid.getRowDom(index)
27211             );
27212             proxy.removeClass('bg-info info');
27213         }
27214         this.fireEvent("rowdeselect", this, index);
27215         this.fireEvent("selectionchange", this);
27216     },
27217
27218     // private
27219     restoreLast : function(){
27220         if(this._last){
27221             this.last = this._last;
27222         }
27223     },
27224
27225     // private
27226     acceptsNav : function(row, col, cm){
27227         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27228     },
27229
27230     // private
27231     onEditorKey : function(field, e){
27232         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27233         if(k == e.TAB){
27234             e.stopEvent();
27235             ed.completeEdit();
27236             if(e.shiftKey){
27237                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27238             }else{
27239                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27240             }
27241         }else if(k == e.ENTER && !e.ctrlKey){
27242             e.stopEvent();
27243             ed.completeEdit();
27244             if(e.shiftKey){
27245                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27246             }else{
27247                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27248             }
27249         }else if(k == e.ESC){
27250             ed.cancelEdit();
27251         }
27252         if(newCell){
27253             g.startEditing(newCell[0], newCell[1]);
27254         }
27255     }
27256 });
27257 /*
27258  * Based on:
27259  * Ext JS Library 1.1.1
27260  * Copyright(c) 2006-2007, Ext JS, LLC.
27261  *
27262  * Originally Released Under LGPL - original licence link has changed is not relivant.
27263  *
27264  * Fork - LGPL
27265  * <script type="text/javascript">
27266  */
27267  
27268 /**
27269  * @class Roo.bootstrap.PagingToolbar
27270  * @extends Roo.bootstrap.NavSimplebar
27271  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27272  * @constructor
27273  * Create a new PagingToolbar
27274  * @param {Object} config The config object
27275  * @param {Roo.data.Store} store
27276  */
27277 Roo.bootstrap.PagingToolbar = function(config)
27278 {
27279     // old args format still supported... - xtype is prefered..
27280         // created from xtype...
27281     
27282     this.ds = config.dataSource;
27283     
27284     if (config.store && !this.ds) {
27285         this.store= Roo.factory(config.store, Roo.data);
27286         this.ds = this.store;
27287         this.ds.xmodule = this.xmodule || false;
27288     }
27289     
27290     this.toolbarItems = [];
27291     if (config.items) {
27292         this.toolbarItems = config.items;
27293     }
27294     
27295     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27296     
27297     this.cursor = 0;
27298     
27299     if (this.ds) { 
27300         this.bind(this.ds);
27301     }
27302     
27303     if (Roo.bootstrap.version == 4) {
27304         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27305     } else {
27306         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27307     }
27308     
27309 };
27310
27311 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27312     /**
27313      * @cfg {Roo.data.Store} dataSource
27314      * The underlying data store providing the paged data
27315      */
27316     /**
27317      * @cfg {String/HTMLElement/Element} container
27318      * container The id or element that will contain the toolbar
27319      */
27320     /**
27321      * @cfg {Boolean} displayInfo
27322      * True to display the displayMsg (defaults to false)
27323      */
27324     /**
27325      * @cfg {Number} pageSize
27326      * The number of records to display per page (defaults to 20)
27327      */
27328     pageSize: 20,
27329     /**
27330      * @cfg {String} displayMsg
27331      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27332      */
27333     displayMsg : 'Displaying {0} - {1} of {2}',
27334     /**
27335      * @cfg {String} emptyMsg
27336      * The message to display when no records are found (defaults to "No data to display")
27337      */
27338     emptyMsg : 'No data to display',
27339     /**
27340      * Customizable piece of the default paging text (defaults to "Page")
27341      * @type String
27342      */
27343     beforePageText : "Page",
27344     /**
27345      * Customizable piece of the default paging text (defaults to "of %0")
27346      * @type String
27347      */
27348     afterPageText : "of {0}",
27349     /**
27350      * Customizable piece of the default paging text (defaults to "First Page")
27351      * @type String
27352      */
27353     firstText : "First Page",
27354     /**
27355      * Customizable piece of the default paging text (defaults to "Previous Page")
27356      * @type String
27357      */
27358     prevText : "Previous Page",
27359     /**
27360      * Customizable piece of the default paging text (defaults to "Next Page")
27361      * @type String
27362      */
27363     nextText : "Next Page",
27364     /**
27365      * Customizable piece of the default paging text (defaults to "Last Page")
27366      * @type String
27367      */
27368     lastText : "Last Page",
27369     /**
27370      * Customizable piece of the default paging text (defaults to "Refresh")
27371      * @type String
27372      */
27373     refreshText : "Refresh",
27374
27375     buttons : false,
27376     // private
27377     onRender : function(ct, position) 
27378     {
27379         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27380         this.navgroup.parentId = this.id;
27381         this.navgroup.onRender(this.el, null);
27382         // add the buttons to the navgroup
27383         
27384         if(this.displayInfo){
27385             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27386             this.displayEl = this.el.select('.x-paging-info', true).first();
27387 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27388 //            this.displayEl = navel.el.select('span',true).first();
27389         }
27390         
27391         var _this = this;
27392         
27393         if(this.buttons){
27394             Roo.each(_this.buttons, function(e){ // this might need to use render????
27395                Roo.factory(e).render(_this.el);
27396             });
27397         }
27398             
27399         Roo.each(_this.toolbarItems, function(e) {
27400             _this.navgroup.addItem(e);
27401         });
27402         
27403         
27404         this.first = this.navgroup.addItem({
27405             tooltip: this.firstText,
27406             cls: "prev btn-outline-secondary",
27407             html : ' <i class="fa fa-step-backward"></i>',
27408             disabled: true,
27409             preventDefault: true,
27410             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27411         });
27412         
27413         this.prev =  this.navgroup.addItem({
27414             tooltip: this.prevText,
27415             cls: "prev btn-outline-secondary",
27416             html : ' <i class="fa fa-backward"></i>',
27417             disabled: true,
27418             preventDefault: true,
27419             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27420         });
27421     //this.addSeparator();
27422         
27423         
27424         var field = this.navgroup.addItem( {
27425             tagtype : 'span',
27426             cls : 'x-paging-position  btn-outline-secondary',
27427              disabled: true,
27428             html : this.beforePageText  +
27429                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27430                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27431          } ); //?? escaped?
27432         
27433         this.field = field.el.select('input', true).first();
27434         this.field.on("keydown", this.onPagingKeydown, this);
27435         this.field.on("focus", function(){this.dom.select();});
27436     
27437     
27438         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27439         //this.field.setHeight(18);
27440         //this.addSeparator();
27441         this.next = this.navgroup.addItem({
27442             tooltip: this.nextText,
27443             cls: "next btn-outline-secondary",
27444             html : ' <i class="fa fa-forward"></i>',
27445             disabled: true,
27446             preventDefault: true,
27447             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27448         });
27449         this.last = this.navgroup.addItem({
27450             tooltip: this.lastText,
27451             html : ' <i class="fa fa-step-forward"></i>',
27452             cls: "next btn-outline-secondary",
27453             disabled: true,
27454             preventDefault: true,
27455             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27456         });
27457     //this.addSeparator();
27458         this.loading = this.navgroup.addItem({
27459             tooltip: this.refreshText,
27460             cls: "btn-outline-secondary",
27461             html : ' <i class="fa fa-refresh"></i>',
27462             preventDefault: true,
27463             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27464         });
27465         
27466     },
27467
27468     // private
27469     updateInfo : function(){
27470         if(this.displayEl){
27471             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27472             var msg = count == 0 ?
27473                 this.emptyMsg :
27474                 String.format(
27475                     this.displayMsg,
27476                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27477                 );
27478             this.displayEl.update(msg);
27479         }
27480     },
27481
27482     // private
27483     onLoad : function(ds, r, o)
27484     {
27485         this.cursor = o.params && o.params.start ? o.params.start : 0;
27486         
27487         var d = this.getPageData(),
27488             ap = d.activePage,
27489             ps = d.pages;
27490         
27491         
27492         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27493         this.field.dom.value = ap;
27494         this.first.setDisabled(ap == 1);
27495         this.prev.setDisabled(ap == 1);
27496         this.next.setDisabled(ap == ps);
27497         this.last.setDisabled(ap == ps);
27498         this.loading.enable();
27499         this.updateInfo();
27500     },
27501
27502     // private
27503     getPageData : function(){
27504         var total = this.ds.getTotalCount();
27505         return {
27506             total : total,
27507             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27508             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27509         };
27510     },
27511
27512     // private
27513     onLoadError : function(){
27514         this.loading.enable();
27515     },
27516
27517     // private
27518     onPagingKeydown : function(e){
27519         var k = e.getKey();
27520         var d = this.getPageData();
27521         if(k == e.RETURN){
27522             var v = this.field.dom.value, pageNum;
27523             if(!v || isNaN(pageNum = parseInt(v, 10))){
27524                 this.field.dom.value = d.activePage;
27525                 return;
27526             }
27527             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27528             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27529             e.stopEvent();
27530         }
27531         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))
27532         {
27533           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27534           this.field.dom.value = pageNum;
27535           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27536           e.stopEvent();
27537         }
27538         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27539         {
27540           var v = this.field.dom.value, pageNum; 
27541           var increment = (e.shiftKey) ? 10 : 1;
27542           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27543                 increment *= -1;
27544           }
27545           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27546             this.field.dom.value = d.activePage;
27547             return;
27548           }
27549           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27550           {
27551             this.field.dom.value = parseInt(v, 10) + increment;
27552             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27553             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27554           }
27555           e.stopEvent();
27556         }
27557     },
27558
27559     // private
27560     beforeLoad : function(){
27561         if(this.loading){
27562             this.loading.disable();
27563         }
27564     },
27565
27566     // private
27567     onClick : function(which){
27568         
27569         var ds = this.ds;
27570         if (!ds) {
27571             return;
27572         }
27573         
27574         switch(which){
27575             case "first":
27576                 ds.load({params:{start: 0, limit: this.pageSize}});
27577             break;
27578             case "prev":
27579                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27580             break;
27581             case "next":
27582                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27583             break;
27584             case "last":
27585                 var total = ds.getTotalCount();
27586                 var extra = total % this.pageSize;
27587                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27588                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27589             break;
27590             case "refresh":
27591                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27592             break;
27593         }
27594     },
27595
27596     /**
27597      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27598      * @param {Roo.data.Store} store The data store to unbind
27599      */
27600     unbind : function(ds){
27601         ds.un("beforeload", this.beforeLoad, this);
27602         ds.un("load", this.onLoad, this);
27603         ds.un("loadexception", this.onLoadError, this);
27604         ds.un("remove", this.updateInfo, this);
27605         ds.un("add", this.updateInfo, this);
27606         this.ds = undefined;
27607     },
27608
27609     /**
27610      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27611      * @param {Roo.data.Store} store The data store to bind
27612      */
27613     bind : function(ds){
27614         ds.on("beforeload", this.beforeLoad, this);
27615         ds.on("load", this.onLoad, this);
27616         ds.on("loadexception", this.onLoadError, this);
27617         ds.on("remove", this.updateInfo, this);
27618         ds.on("add", this.updateInfo, this);
27619         this.ds = ds;
27620     }
27621 });/*
27622  * - LGPL
27623  *
27624  * element
27625  * 
27626  */
27627
27628 /**
27629  * @class Roo.bootstrap.MessageBar
27630  * @extends Roo.bootstrap.Component
27631  * Bootstrap MessageBar class
27632  * @cfg {String} html contents of the MessageBar
27633  * @cfg {String} weight (info | success | warning | danger) default info
27634  * @cfg {String} beforeClass insert the bar before the given class
27635  * @cfg {Boolean} closable (true | false) default false
27636  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27637  * 
27638  * @constructor
27639  * Create a new Element
27640  * @param {Object} config The config object
27641  */
27642
27643 Roo.bootstrap.MessageBar = function(config){
27644     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27645 };
27646
27647 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27648     
27649     html: '',
27650     weight: 'info',
27651     closable: false,
27652     fixed: false,
27653     beforeClass: 'bootstrap-sticky-wrap',
27654     
27655     getAutoCreate : function(){
27656         
27657         var cfg = {
27658             tag: 'div',
27659             cls: 'alert alert-dismissable alert-' + this.weight,
27660             cn: [
27661                 {
27662                     tag: 'span',
27663                     cls: 'message',
27664                     html: this.html || ''
27665                 }
27666             ]
27667         };
27668         
27669         if(this.fixed){
27670             cfg.cls += ' alert-messages-fixed';
27671         }
27672         
27673         if(this.closable){
27674             cfg.cn.push({
27675                 tag: 'button',
27676                 cls: 'close',
27677                 html: 'x'
27678             });
27679         }
27680         
27681         return cfg;
27682     },
27683     
27684     onRender : function(ct, position)
27685     {
27686         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27687         
27688         if(!this.el){
27689             var cfg = Roo.apply({},  this.getAutoCreate());
27690             cfg.id = Roo.id();
27691             
27692             if (this.cls) {
27693                 cfg.cls += ' ' + this.cls;
27694             }
27695             if (this.style) {
27696                 cfg.style = this.style;
27697             }
27698             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27699             
27700             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27701         }
27702         
27703         this.el.select('>button.close').on('click', this.hide, this);
27704         
27705     },
27706     
27707     show : function()
27708     {
27709         if (!this.rendered) {
27710             this.render();
27711         }
27712         
27713         this.el.show();
27714         
27715         this.fireEvent('show', this);
27716         
27717     },
27718     
27719     hide : function()
27720     {
27721         if (!this.rendered) {
27722             this.render();
27723         }
27724         
27725         this.el.hide();
27726         
27727         this.fireEvent('hide', this);
27728     },
27729     
27730     update : function()
27731     {
27732 //        var e = this.el.dom.firstChild;
27733 //        
27734 //        if(this.closable){
27735 //            e = e.nextSibling;
27736 //        }
27737 //        
27738 //        e.data = this.html || '';
27739
27740         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27741     }
27742    
27743 });
27744
27745  
27746
27747      /*
27748  * - LGPL
27749  *
27750  * Graph
27751  * 
27752  */
27753
27754
27755 /**
27756  * @class Roo.bootstrap.Graph
27757  * @extends Roo.bootstrap.Component
27758  * Bootstrap Graph class
27759 > Prameters
27760  -sm {number} sm 4
27761  -md {number} md 5
27762  @cfg {String} graphtype  bar | vbar | pie
27763  @cfg {number} g_x coodinator | centre x (pie)
27764  @cfg {number} g_y coodinator | centre y (pie)
27765  @cfg {number} g_r radius (pie)
27766  @cfg {number} g_height height of the chart (respected by all elements in the set)
27767  @cfg {number} g_width width of the chart (respected by all elements in the set)
27768  @cfg {Object} title The title of the chart
27769     
27770  -{Array}  values
27771  -opts (object) options for the chart 
27772      o {
27773      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27774      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27775      o vgutter (number)
27776      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.
27777      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27778      o to
27779      o stretch (boolean)
27780      o }
27781  -opts (object) options for the pie
27782      o{
27783      o cut
27784      o startAngle (number)
27785      o endAngle (number)
27786      } 
27787  *
27788  * @constructor
27789  * Create a new Input
27790  * @param {Object} config The config object
27791  */
27792
27793 Roo.bootstrap.Graph = function(config){
27794     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27795     
27796     this.addEvents({
27797         // img events
27798         /**
27799          * @event click
27800          * The img click event for the img.
27801          * @param {Roo.EventObject} e
27802          */
27803         "click" : true
27804     });
27805 };
27806
27807 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27808     
27809     sm: 4,
27810     md: 5,
27811     graphtype: 'bar',
27812     g_height: 250,
27813     g_width: 400,
27814     g_x: 50,
27815     g_y: 50,
27816     g_r: 30,
27817     opts:{
27818         //g_colors: this.colors,
27819         g_type: 'soft',
27820         g_gutter: '20%'
27821
27822     },
27823     title : false,
27824
27825     getAutoCreate : function(){
27826         
27827         var cfg = {
27828             tag: 'div',
27829             html : null
27830         };
27831         
27832         
27833         return  cfg;
27834     },
27835
27836     onRender : function(ct,position){
27837         
27838         
27839         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27840         
27841         if (typeof(Raphael) == 'undefined') {
27842             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27843             return;
27844         }
27845         
27846         this.raphael = Raphael(this.el.dom);
27847         
27848                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27849                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27850                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27851                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27852                 /*
27853                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27854                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27855                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27856                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27857                 
27858                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27859                 r.barchart(330, 10, 300, 220, data1);
27860                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27861                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27862                 */
27863                 
27864                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27865                 // r.barchart(30, 30, 560, 250,  xdata, {
27866                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27867                 //     axis : "0 0 1 1",
27868                 //     axisxlabels :  xdata
27869                 //     //yvalues : cols,
27870                    
27871                 // });
27872 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27873 //        
27874 //        this.load(null,xdata,{
27875 //                axis : "0 0 1 1",
27876 //                axisxlabels :  xdata
27877 //                });
27878
27879     },
27880
27881     load : function(graphtype,xdata,opts)
27882     {
27883         this.raphael.clear();
27884         if(!graphtype) {
27885             graphtype = this.graphtype;
27886         }
27887         if(!opts){
27888             opts = this.opts;
27889         }
27890         var r = this.raphael,
27891             fin = function () {
27892                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27893             },
27894             fout = function () {
27895                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27896             },
27897             pfin = function() {
27898                 this.sector.stop();
27899                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27900
27901                 if (this.label) {
27902                     this.label[0].stop();
27903                     this.label[0].attr({ r: 7.5 });
27904                     this.label[1].attr({ "font-weight": 800 });
27905                 }
27906             },
27907             pfout = function() {
27908                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27909
27910                 if (this.label) {
27911                     this.label[0].animate({ r: 5 }, 500, "bounce");
27912                     this.label[1].attr({ "font-weight": 400 });
27913                 }
27914             };
27915
27916         switch(graphtype){
27917             case 'bar':
27918                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27919                 break;
27920             case 'hbar':
27921                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27922                 break;
27923             case 'pie':
27924 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27925 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27926 //            
27927                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27928                 
27929                 break;
27930
27931         }
27932         
27933         if(this.title){
27934             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27935         }
27936         
27937     },
27938     
27939     setTitle: function(o)
27940     {
27941         this.title = o;
27942     },
27943     
27944     initEvents: function() {
27945         
27946         if(!this.href){
27947             this.el.on('click', this.onClick, this);
27948         }
27949     },
27950     
27951     onClick : function(e)
27952     {
27953         Roo.log('img onclick');
27954         this.fireEvent('click', this, e);
27955     }
27956    
27957 });
27958
27959  
27960 /*
27961  * - LGPL
27962  *
27963  * numberBox
27964  * 
27965  */
27966 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27967
27968 /**
27969  * @class Roo.bootstrap.dash.NumberBox
27970  * @extends Roo.bootstrap.Component
27971  * Bootstrap NumberBox class
27972  * @cfg {String} headline Box headline
27973  * @cfg {String} content Box content
27974  * @cfg {String} icon Box icon
27975  * @cfg {String} footer Footer text
27976  * @cfg {String} fhref Footer href
27977  * 
27978  * @constructor
27979  * Create a new NumberBox
27980  * @param {Object} config The config object
27981  */
27982
27983
27984 Roo.bootstrap.dash.NumberBox = function(config){
27985     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27986     
27987 };
27988
27989 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27990     
27991     headline : '',
27992     content : '',
27993     icon : '',
27994     footer : '',
27995     fhref : '',
27996     ficon : '',
27997     
27998     getAutoCreate : function(){
27999         
28000         var cfg = {
28001             tag : 'div',
28002             cls : 'small-box ',
28003             cn : [
28004                 {
28005                     tag : 'div',
28006                     cls : 'inner',
28007                     cn :[
28008                         {
28009                             tag : 'h3',
28010                             cls : 'roo-headline',
28011                             html : this.headline
28012                         },
28013                         {
28014                             tag : 'p',
28015                             cls : 'roo-content',
28016                             html : this.content
28017                         }
28018                     ]
28019                 }
28020             ]
28021         };
28022         
28023         if(this.icon){
28024             cfg.cn.push({
28025                 tag : 'div',
28026                 cls : 'icon',
28027                 cn :[
28028                     {
28029                         tag : 'i',
28030                         cls : 'ion ' + this.icon
28031                     }
28032                 ]
28033             });
28034         }
28035         
28036         if(this.footer){
28037             var footer = {
28038                 tag : 'a',
28039                 cls : 'small-box-footer',
28040                 href : this.fhref || '#',
28041                 html : this.footer
28042             };
28043             
28044             cfg.cn.push(footer);
28045             
28046         }
28047         
28048         return  cfg;
28049     },
28050
28051     onRender : function(ct,position){
28052         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28053
28054
28055        
28056                 
28057     },
28058
28059     setHeadline: function (value)
28060     {
28061         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28062     },
28063     
28064     setFooter: function (value, href)
28065     {
28066         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28067         
28068         if(href){
28069             this.el.select('a.small-box-footer',true).first().attr('href', href);
28070         }
28071         
28072     },
28073
28074     setContent: function (value)
28075     {
28076         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28077     },
28078
28079     initEvents: function() 
28080     {   
28081         
28082     }
28083     
28084 });
28085
28086  
28087 /*
28088  * - LGPL
28089  *
28090  * TabBox
28091  * 
28092  */
28093 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28094
28095 /**
28096  * @class Roo.bootstrap.dash.TabBox
28097  * @extends Roo.bootstrap.Component
28098  * Bootstrap TabBox class
28099  * @cfg {String} title Title of the TabBox
28100  * @cfg {String} icon Icon of the TabBox
28101  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28102  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28103  * 
28104  * @constructor
28105  * Create a new TabBox
28106  * @param {Object} config The config object
28107  */
28108
28109
28110 Roo.bootstrap.dash.TabBox = function(config){
28111     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28112     this.addEvents({
28113         // raw events
28114         /**
28115          * @event addpane
28116          * When a pane is added
28117          * @param {Roo.bootstrap.dash.TabPane} pane
28118          */
28119         "addpane" : true,
28120         /**
28121          * @event activatepane
28122          * When a pane is activated
28123          * @param {Roo.bootstrap.dash.TabPane} pane
28124          */
28125         "activatepane" : true
28126         
28127          
28128     });
28129     
28130     this.panes = [];
28131 };
28132
28133 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28134
28135     title : '',
28136     icon : false,
28137     showtabs : true,
28138     tabScrollable : false,
28139     
28140     getChildContainer : function()
28141     {
28142         return this.el.select('.tab-content', true).first();
28143     },
28144     
28145     getAutoCreate : function(){
28146         
28147         var header = {
28148             tag: 'li',
28149             cls: 'pull-left header',
28150             html: this.title,
28151             cn : []
28152         };
28153         
28154         if(this.icon){
28155             header.cn.push({
28156                 tag: 'i',
28157                 cls: 'fa ' + this.icon
28158             });
28159         }
28160         
28161         var h = {
28162             tag: 'ul',
28163             cls: 'nav nav-tabs pull-right',
28164             cn: [
28165                 header
28166             ]
28167         };
28168         
28169         if(this.tabScrollable){
28170             h = {
28171                 tag: 'div',
28172                 cls: 'tab-header',
28173                 cn: [
28174                     {
28175                         tag: 'ul',
28176                         cls: 'nav nav-tabs pull-right',
28177                         cn: [
28178                             header
28179                         ]
28180                     }
28181                 ]
28182             };
28183         }
28184         
28185         var cfg = {
28186             tag: 'div',
28187             cls: 'nav-tabs-custom',
28188             cn: [
28189                 h,
28190                 {
28191                     tag: 'div',
28192                     cls: 'tab-content no-padding',
28193                     cn: []
28194                 }
28195             ]
28196         };
28197
28198         return  cfg;
28199     },
28200     initEvents : function()
28201     {
28202         //Roo.log('add add pane handler');
28203         this.on('addpane', this.onAddPane, this);
28204     },
28205      /**
28206      * Updates the box title
28207      * @param {String} html to set the title to.
28208      */
28209     setTitle : function(value)
28210     {
28211         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28212     },
28213     onAddPane : function(pane)
28214     {
28215         this.panes.push(pane);
28216         //Roo.log('addpane');
28217         //Roo.log(pane);
28218         // tabs are rendere left to right..
28219         if(!this.showtabs){
28220             return;
28221         }
28222         
28223         var ctr = this.el.select('.nav-tabs', true).first();
28224          
28225          
28226         var existing = ctr.select('.nav-tab',true);
28227         var qty = existing.getCount();;
28228         
28229         
28230         var tab = ctr.createChild({
28231             tag : 'li',
28232             cls : 'nav-tab' + (qty ? '' : ' active'),
28233             cn : [
28234                 {
28235                     tag : 'a',
28236                     href:'#',
28237                     html : pane.title
28238                 }
28239             ]
28240         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28241         pane.tab = tab;
28242         
28243         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28244         if (!qty) {
28245             pane.el.addClass('active');
28246         }
28247         
28248                 
28249     },
28250     onTabClick : function(ev,un,ob,pane)
28251     {
28252         //Roo.log('tab - prev default');
28253         ev.preventDefault();
28254         
28255         
28256         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28257         pane.tab.addClass('active');
28258         //Roo.log(pane.title);
28259         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28260         // technically we should have a deactivate event.. but maybe add later.
28261         // and it should not de-activate the selected tab...
28262         this.fireEvent('activatepane', pane);
28263         pane.el.addClass('active');
28264         pane.fireEvent('activate');
28265         
28266         
28267     },
28268     
28269     getActivePane : function()
28270     {
28271         var r = false;
28272         Roo.each(this.panes, function(p) {
28273             if(p.el.hasClass('active')){
28274                 r = p;
28275                 return false;
28276             }
28277             
28278             return;
28279         });
28280         
28281         return r;
28282     }
28283     
28284     
28285 });
28286
28287  
28288 /*
28289  * - LGPL
28290  *
28291  * Tab pane
28292  * 
28293  */
28294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28295 /**
28296  * @class Roo.bootstrap.TabPane
28297  * @extends Roo.bootstrap.Component
28298  * Bootstrap TabPane class
28299  * @cfg {Boolean} active (false | true) Default false
28300  * @cfg {String} title title of panel
28301
28302  * 
28303  * @constructor
28304  * Create a new TabPane
28305  * @param {Object} config The config object
28306  */
28307
28308 Roo.bootstrap.dash.TabPane = function(config){
28309     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28310     
28311     this.addEvents({
28312         // raw events
28313         /**
28314          * @event activate
28315          * When a pane is activated
28316          * @param {Roo.bootstrap.dash.TabPane} pane
28317          */
28318         "activate" : true
28319          
28320     });
28321 };
28322
28323 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28324     
28325     active : false,
28326     title : '',
28327     
28328     // the tabBox that this is attached to.
28329     tab : false,
28330      
28331     getAutoCreate : function() 
28332     {
28333         var cfg = {
28334             tag: 'div',
28335             cls: 'tab-pane'
28336         };
28337         
28338         if(this.active){
28339             cfg.cls += ' active';
28340         }
28341         
28342         return cfg;
28343     },
28344     initEvents  : function()
28345     {
28346         //Roo.log('trigger add pane handler');
28347         this.parent().fireEvent('addpane', this)
28348     },
28349     
28350      /**
28351      * Updates the tab title 
28352      * @param {String} html to set the title to.
28353      */
28354     setTitle: function(str)
28355     {
28356         if (!this.tab) {
28357             return;
28358         }
28359         this.title = str;
28360         this.tab.select('a', true).first().dom.innerHTML = str;
28361         
28362     }
28363     
28364     
28365     
28366 });
28367
28368  
28369
28370
28371  /*
28372  * - LGPL
28373  *
28374  * menu
28375  * 
28376  */
28377 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28378
28379 /**
28380  * @class Roo.bootstrap.menu.Menu
28381  * @extends Roo.bootstrap.Component
28382  * Bootstrap Menu class - container for Menu
28383  * @cfg {String} html Text of the menu
28384  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28385  * @cfg {String} icon Font awesome icon
28386  * @cfg {String} pos Menu align to (top | bottom) default bottom
28387  * 
28388  * 
28389  * @constructor
28390  * Create a new Menu
28391  * @param {Object} config The config object
28392  */
28393
28394
28395 Roo.bootstrap.menu.Menu = function(config){
28396     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28397     
28398     this.addEvents({
28399         /**
28400          * @event beforeshow
28401          * Fires before this menu is displayed
28402          * @param {Roo.bootstrap.menu.Menu} this
28403          */
28404         beforeshow : true,
28405         /**
28406          * @event beforehide
28407          * Fires before this menu is hidden
28408          * @param {Roo.bootstrap.menu.Menu} this
28409          */
28410         beforehide : true,
28411         /**
28412          * @event show
28413          * Fires after this menu is displayed
28414          * @param {Roo.bootstrap.menu.Menu} this
28415          */
28416         show : true,
28417         /**
28418          * @event hide
28419          * Fires after this menu is hidden
28420          * @param {Roo.bootstrap.menu.Menu} this
28421          */
28422         hide : true,
28423         /**
28424          * @event click
28425          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28426          * @param {Roo.bootstrap.menu.Menu} this
28427          * @param {Roo.EventObject} e
28428          */
28429         click : true
28430     });
28431     
28432 };
28433
28434 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28435     
28436     submenu : false,
28437     html : '',
28438     weight : 'default',
28439     icon : false,
28440     pos : 'bottom',
28441     
28442     
28443     getChildContainer : function() {
28444         if(this.isSubMenu){
28445             return this.el;
28446         }
28447         
28448         return this.el.select('ul.dropdown-menu', true).first();  
28449     },
28450     
28451     getAutoCreate : function()
28452     {
28453         var text = [
28454             {
28455                 tag : 'span',
28456                 cls : 'roo-menu-text',
28457                 html : this.html
28458             }
28459         ];
28460         
28461         if(this.icon){
28462             text.unshift({
28463                 tag : 'i',
28464                 cls : 'fa ' + this.icon
28465             })
28466         }
28467         
28468         
28469         var cfg = {
28470             tag : 'div',
28471             cls : 'btn-group',
28472             cn : [
28473                 {
28474                     tag : 'button',
28475                     cls : 'dropdown-button btn btn-' + this.weight,
28476                     cn : text
28477                 },
28478                 {
28479                     tag : 'button',
28480                     cls : 'dropdown-toggle btn btn-' + this.weight,
28481                     cn : [
28482                         {
28483                             tag : 'span',
28484                             cls : 'caret'
28485                         }
28486                     ]
28487                 },
28488                 {
28489                     tag : 'ul',
28490                     cls : 'dropdown-menu'
28491                 }
28492             ]
28493             
28494         };
28495         
28496         if(this.pos == 'top'){
28497             cfg.cls += ' dropup';
28498         }
28499         
28500         if(this.isSubMenu){
28501             cfg = {
28502                 tag : 'ul',
28503                 cls : 'dropdown-menu'
28504             }
28505         }
28506         
28507         return cfg;
28508     },
28509     
28510     onRender : function(ct, position)
28511     {
28512         this.isSubMenu = ct.hasClass('dropdown-submenu');
28513         
28514         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28515     },
28516     
28517     initEvents : function() 
28518     {
28519         if(this.isSubMenu){
28520             return;
28521         }
28522         
28523         this.hidden = true;
28524         
28525         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28526         this.triggerEl.on('click', this.onTriggerPress, this);
28527         
28528         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28529         this.buttonEl.on('click', this.onClick, this);
28530         
28531     },
28532     
28533     list : function()
28534     {
28535         if(this.isSubMenu){
28536             return this.el;
28537         }
28538         
28539         return this.el.select('ul.dropdown-menu', true).first();
28540     },
28541     
28542     onClick : function(e)
28543     {
28544         this.fireEvent("click", this, e);
28545     },
28546     
28547     onTriggerPress  : function(e)
28548     {   
28549         if (this.isVisible()) {
28550             this.hide();
28551         } else {
28552             this.show();
28553         }
28554     },
28555     
28556     isVisible : function(){
28557         return !this.hidden;
28558     },
28559     
28560     show : function()
28561     {
28562         this.fireEvent("beforeshow", this);
28563         
28564         this.hidden = false;
28565         this.el.addClass('open');
28566         
28567         Roo.get(document).on("mouseup", this.onMouseUp, this);
28568         
28569         this.fireEvent("show", this);
28570         
28571         
28572     },
28573     
28574     hide : function()
28575     {
28576         this.fireEvent("beforehide", this);
28577         
28578         this.hidden = true;
28579         this.el.removeClass('open');
28580         
28581         Roo.get(document).un("mouseup", this.onMouseUp);
28582         
28583         this.fireEvent("hide", this);
28584     },
28585     
28586     onMouseUp : function()
28587     {
28588         this.hide();
28589     }
28590     
28591 });
28592
28593  
28594  /*
28595  * - LGPL
28596  *
28597  * menu item
28598  * 
28599  */
28600 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28601
28602 /**
28603  * @class Roo.bootstrap.menu.Item
28604  * @extends Roo.bootstrap.Component
28605  * Bootstrap MenuItem class
28606  * @cfg {Boolean} submenu (true | false) default false
28607  * @cfg {String} html text of the item
28608  * @cfg {String} href the link
28609  * @cfg {Boolean} disable (true | false) default false
28610  * @cfg {Boolean} preventDefault (true | false) default true
28611  * @cfg {String} icon Font awesome icon
28612  * @cfg {String} pos Submenu align to (left | right) default right 
28613  * 
28614  * 
28615  * @constructor
28616  * Create a new Item
28617  * @param {Object} config The config object
28618  */
28619
28620
28621 Roo.bootstrap.menu.Item = function(config){
28622     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28623     this.addEvents({
28624         /**
28625          * @event mouseover
28626          * Fires when the mouse is hovering over this menu
28627          * @param {Roo.bootstrap.menu.Item} this
28628          * @param {Roo.EventObject} e
28629          */
28630         mouseover : true,
28631         /**
28632          * @event mouseout
28633          * Fires when the mouse exits this menu
28634          * @param {Roo.bootstrap.menu.Item} this
28635          * @param {Roo.EventObject} e
28636          */
28637         mouseout : true,
28638         // raw events
28639         /**
28640          * @event click
28641          * The raw click event for the entire grid.
28642          * @param {Roo.EventObject} e
28643          */
28644         click : true
28645     });
28646 };
28647
28648 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28649     
28650     submenu : false,
28651     href : '',
28652     html : '',
28653     preventDefault: true,
28654     disable : false,
28655     icon : false,
28656     pos : 'right',
28657     
28658     getAutoCreate : function()
28659     {
28660         var text = [
28661             {
28662                 tag : 'span',
28663                 cls : 'roo-menu-item-text',
28664                 html : this.html
28665             }
28666         ];
28667         
28668         if(this.icon){
28669             text.unshift({
28670                 tag : 'i',
28671                 cls : 'fa ' + this.icon
28672             })
28673         }
28674         
28675         var cfg = {
28676             tag : 'li',
28677             cn : [
28678                 {
28679                     tag : 'a',
28680                     href : this.href || '#',
28681                     cn : text
28682                 }
28683             ]
28684         };
28685         
28686         if(this.disable){
28687             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28688         }
28689         
28690         if(this.submenu){
28691             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28692             
28693             if(this.pos == 'left'){
28694                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28695             }
28696         }
28697         
28698         return cfg;
28699     },
28700     
28701     initEvents : function() 
28702     {
28703         this.el.on('mouseover', this.onMouseOver, this);
28704         this.el.on('mouseout', this.onMouseOut, this);
28705         
28706         this.el.select('a', true).first().on('click', this.onClick, this);
28707         
28708     },
28709     
28710     onClick : function(e)
28711     {
28712         if(this.preventDefault){
28713             e.preventDefault();
28714         }
28715         
28716         this.fireEvent("click", this, e);
28717     },
28718     
28719     onMouseOver : function(e)
28720     {
28721         if(this.submenu && this.pos == 'left'){
28722             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28723         }
28724         
28725         this.fireEvent("mouseover", this, e);
28726     },
28727     
28728     onMouseOut : function(e)
28729     {
28730         this.fireEvent("mouseout", this, e);
28731     }
28732 });
28733
28734  
28735
28736  /*
28737  * - LGPL
28738  *
28739  * menu separator
28740  * 
28741  */
28742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28743
28744 /**
28745  * @class Roo.bootstrap.menu.Separator
28746  * @extends Roo.bootstrap.Component
28747  * Bootstrap Separator class
28748  * 
28749  * @constructor
28750  * Create a new Separator
28751  * @param {Object} config The config object
28752  */
28753
28754
28755 Roo.bootstrap.menu.Separator = function(config){
28756     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28757 };
28758
28759 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28760     
28761     getAutoCreate : function(){
28762         var cfg = {
28763             tag : 'li',
28764             cls: 'divider'
28765         };
28766         
28767         return cfg;
28768     }
28769    
28770 });
28771
28772  
28773
28774  /*
28775  * - LGPL
28776  *
28777  * Tooltip
28778  * 
28779  */
28780
28781 /**
28782  * @class Roo.bootstrap.Tooltip
28783  * Bootstrap Tooltip class
28784  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28785  * to determine which dom element triggers the tooltip.
28786  * 
28787  * It needs to add support for additional attributes like tooltip-position
28788  * 
28789  * @constructor
28790  * Create a new Toolti
28791  * @param {Object} config The config object
28792  */
28793
28794 Roo.bootstrap.Tooltip = function(config){
28795     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28796     
28797     this.alignment = Roo.bootstrap.Tooltip.alignment;
28798     
28799     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28800         this.alignment = config.alignment;
28801     }
28802     
28803 };
28804
28805 Roo.apply(Roo.bootstrap.Tooltip, {
28806     /**
28807      * @function init initialize tooltip monitoring.
28808      * @static
28809      */
28810     currentEl : false,
28811     currentTip : false,
28812     currentRegion : false,
28813     
28814     //  init : delay?
28815     
28816     init : function()
28817     {
28818         Roo.get(document).on('mouseover', this.enter ,this);
28819         Roo.get(document).on('mouseout', this.leave, this);
28820          
28821         
28822         this.currentTip = new Roo.bootstrap.Tooltip();
28823     },
28824     
28825     enter : function(ev)
28826     {
28827         var dom = ev.getTarget();
28828         
28829         //Roo.log(['enter',dom]);
28830         var el = Roo.fly(dom);
28831         if (this.currentEl) {
28832             //Roo.log(dom);
28833             //Roo.log(this.currentEl);
28834             //Roo.log(this.currentEl.contains(dom));
28835             if (this.currentEl == el) {
28836                 return;
28837             }
28838             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28839                 return;
28840             }
28841
28842         }
28843         
28844         if (this.currentTip.el) {
28845             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28846         }    
28847         //Roo.log(ev);
28848         
28849         if(!el || el.dom == document){
28850             return;
28851         }
28852         
28853         var bindEl = el;
28854         
28855         // you can not look for children, as if el is the body.. then everythign is the child..
28856         if (!el.attr('tooltip')) { //
28857             if (!el.select("[tooltip]").elements.length) {
28858                 return;
28859             }
28860             // is the mouse over this child...?
28861             bindEl = el.select("[tooltip]").first();
28862             var xy = ev.getXY();
28863             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28864                 //Roo.log("not in region.");
28865                 return;
28866             }
28867             //Roo.log("child element over..");
28868             
28869         }
28870         this.currentEl = bindEl;
28871         this.currentTip.bind(bindEl);
28872         this.currentRegion = Roo.lib.Region.getRegion(dom);
28873         this.currentTip.enter();
28874         
28875     },
28876     leave : function(ev)
28877     {
28878         var dom = ev.getTarget();
28879         //Roo.log(['leave',dom]);
28880         if (!this.currentEl) {
28881             return;
28882         }
28883         
28884         
28885         if (dom != this.currentEl.dom) {
28886             return;
28887         }
28888         var xy = ev.getXY();
28889         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28890             return;
28891         }
28892         // only activate leave if mouse cursor is outside... bounding box..
28893         
28894         
28895         
28896         
28897         if (this.currentTip) {
28898             this.currentTip.leave();
28899         }
28900         //Roo.log('clear currentEl');
28901         this.currentEl = false;
28902         
28903         
28904     },
28905     alignment : {
28906         'left' : ['r-l', [-2,0], 'right'],
28907         'right' : ['l-r', [2,0], 'left'],
28908         'bottom' : ['t-b', [0,2], 'top'],
28909         'top' : [ 'b-t', [0,-2], 'bottom']
28910     }
28911     
28912 });
28913
28914
28915 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28916     
28917     
28918     bindEl : false,
28919     
28920     delay : null, // can be { show : 300 , hide: 500}
28921     
28922     timeout : null,
28923     
28924     hoverState : null, //???
28925     
28926     placement : 'bottom', 
28927     
28928     alignment : false,
28929     
28930     getAutoCreate : function(){
28931     
28932         var cfg = {
28933            cls : 'tooltip',   
28934            role : 'tooltip',
28935            cn : [
28936                 {
28937                     cls : 'tooltip-arrow arrow'
28938                 },
28939                 {
28940                     cls : 'tooltip-inner'
28941                 }
28942            ]
28943         };
28944         
28945         return cfg;
28946     },
28947     bind : function(el)
28948     {
28949         this.bindEl = el;
28950     },
28951     
28952     initEvents : function()
28953     {
28954         this.arrowEl = this.el.select('.arrow', true).first();
28955         this.innerEl = this.el.select('.tooltip-inner', true).first();
28956     },
28957     
28958     enter : function () {
28959        
28960         if (this.timeout != null) {
28961             clearTimeout(this.timeout);
28962         }
28963         
28964         this.hoverState = 'in';
28965          //Roo.log("enter - show");
28966         if (!this.delay || !this.delay.show) {
28967             this.show();
28968             return;
28969         }
28970         var _t = this;
28971         this.timeout = setTimeout(function () {
28972             if (_t.hoverState == 'in') {
28973                 _t.show();
28974             }
28975         }, this.delay.show);
28976     },
28977     leave : function()
28978     {
28979         clearTimeout(this.timeout);
28980     
28981         this.hoverState = 'out';
28982          if (!this.delay || !this.delay.hide) {
28983             this.hide();
28984             return;
28985         }
28986        
28987         var _t = this;
28988         this.timeout = setTimeout(function () {
28989             //Roo.log("leave - timeout");
28990             
28991             if (_t.hoverState == 'out') {
28992                 _t.hide();
28993                 Roo.bootstrap.Tooltip.currentEl = false;
28994             }
28995         }, delay);
28996     },
28997     
28998     show : function (msg)
28999     {
29000         if (!this.el) {
29001             this.render(document.body);
29002         }
29003         // set content.
29004         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29005         
29006         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29007         
29008         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29009         
29010         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29011                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29012         
29013         var placement = typeof this.placement == 'function' ?
29014             this.placement.call(this, this.el, on_el) :
29015             this.placement;
29016             
29017         var autoToken = /\s?auto?\s?/i;
29018         var autoPlace = autoToken.test(placement);
29019         if (autoPlace) {
29020             placement = placement.replace(autoToken, '') || 'top';
29021         }
29022         
29023         //this.el.detach()
29024         //this.el.setXY([0,0]);
29025         this.el.show();
29026         //this.el.dom.style.display='block';
29027         
29028         //this.el.appendTo(on_el);
29029         
29030         var p = this.getPosition();
29031         var box = this.el.getBox();
29032         
29033         if (autoPlace) {
29034             // fixme..
29035         }
29036         
29037         var align = this.alignment[placement];
29038         
29039         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29040         
29041         if(placement == 'top' || placement == 'bottom'){
29042             if(xy[0] < 0){
29043                 placement = 'right';
29044             }
29045             
29046             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29047                 placement = 'left';
29048             }
29049             
29050             var scroll = Roo.select('body', true).first().getScroll();
29051             
29052             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29053                 placement = 'top';
29054             }
29055             
29056             align = this.alignment[placement];
29057             
29058             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29059             
29060         }
29061         
29062         this.el.alignTo(this.bindEl, align[0],align[1]);
29063         //var arrow = this.el.select('.arrow',true).first();
29064         //arrow.set(align[2], 
29065         
29066         this.el.addClass(placement);
29067         this.el.addClass("bs-tooltip-"+ placement);
29068         
29069         this.el.addClass('in fade show');
29070         
29071         this.hoverState = null;
29072         
29073         if (this.el.hasClass('fade')) {
29074             // fade it?
29075         }
29076         
29077         
29078         
29079         
29080         
29081     },
29082     hide : function()
29083     {
29084          
29085         if (!this.el) {
29086             return;
29087         }
29088         //this.el.setXY([0,0]);
29089         this.el.removeClass(['show', 'in']);
29090         //this.el.hide();
29091         
29092     }
29093     
29094 });
29095  
29096
29097  /*
29098  * - LGPL
29099  *
29100  * Location Picker
29101  * 
29102  */
29103
29104 /**
29105  * @class Roo.bootstrap.LocationPicker
29106  * @extends Roo.bootstrap.Component
29107  * Bootstrap LocationPicker class
29108  * @cfg {Number} latitude Position when init default 0
29109  * @cfg {Number} longitude Position when init default 0
29110  * @cfg {Number} zoom default 15
29111  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29112  * @cfg {Boolean} mapTypeControl default false
29113  * @cfg {Boolean} disableDoubleClickZoom default false
29114  * @cfg {Boolean} scrollwheel default true
29115  * @cfg {Boolean} streetViewControl default false
29116  * @cfg {Number} radius default 0
29117  * @cfg {String} locationName
29118  * @cfg {Boolean} draggable default true
29119  * @cfg {Boolean} enableAutocomplete default false
29120  * @cfg {Boolean} enableReverseGeocode default true
29121  * @cfg {String} markerTitle
29122  * 
29123  * @constructor
29124  * Create a new LocationPicker
29125  * @param {Object} config The config object
29126  */
29127
29128
29129 Roo.bootstrap.LocationPicker = function(config){
29130     
29131     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29132     
29133     this.addEvents({
29134         /**
29135          * @event initial
29136          * Fires when the picker initialized.
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          * @param {Google Location} location
29139          */
29140         initial : true,
29141         /**
29142          * @event positionchanged
29143          * Fires when the picker position changed.
29144          * @param {Roo.bootstrap.LocationPicker} this
29145          * @param {Google Location} location
29146          */
29147         positionchanged : true,
29148         /**
29149          * @event resize
29150          * Fires when the map resize.
29151          * @param {Roo.bootstrap.LocationPicker} this
29152          */
29153         resize : true,
29154         /**
29155          * @event show
29156          * Fires when the map show.
29157          * @param {Roo.bootstrap.LocationPicker} this
29158          */
29159         show : true,
29160         /**
29161          * @event hide
29162          * Fires when the map hide.
29163          * @param {Roo.bootstrap.LocationPicker} this
29164          */
29165         hide : true,
29166         /**
29167          * @event mapClick
29168          * Fires when click the map.
29169          * @param {Roo.bootstrap.LocationPicker} this
29170          * @param {Map event} e
29171          */
29172         mapClick : true,
29173         /**
29174          * @event mapRightClick
29175          * Fires when right click the map.
29176          * @param {Roo.bootstrap.LocationPicker} this
29177          * @param {Map event} e
29178          */
29179         mapRightClick : true,
29180         /**
29181          * @event markerClick
29182          * Fires when click the marker.
29183          * @param {Roo.bootstrap.LocationPicker} this
29184          * @param {Map event} e
29185          */
29186         markerClick : true,
29187         /**
29188          * @event markerRightClick
29189          * Fires when right click the marker.
29190          * @param {Roo.bootstrap.LocationPicker} this
29191          * @param {Map event} e
29192          */
29193         markerRightClick : true,
29194         /**
29195          * @event OverlayViewDraw
29196          * Fires when OverlayView Draw
29197          * @param {Roo.bootstrap.LocationPicker} this
29198          */
29199         OverlayViewDraw : true,
29200         /**
29201          * @event OverlayViewOnAdd
29202          * Fires when OverlayView Draw
29203          * @param {Roo.bootstrap.LocationPicker} this
29204          */
29205         OverlayViewOnAdd : true,
29206         /**
29207          * @event OverlayViewOnRemove
29208          * Fires when OverlayView Draw
29209          * @param {Roo.bootstrap.LocationPicker} this
29210          */
29211         OverlayViewOnRemove : true,
29212         /**
29213          * @event OverlayViewShow
29214          * Fires when OverlayView Draw
29215          * @param {Roo.bootstrap.LocationPicker} this
29216          * @param {Pixel} cpx
29217          */
29218         OverlayViewShow : true,
29219         /**
29220          * @event OverlayViewHide
29221          * Fires when OverlayView Draw
29222          * @param {Roo.bootstrap.LocationPicker} this
29223          */
29224         OverlayViewHide : true,
29225         /**
29226          * @event loadexception
29227          * Fires when load google lib failed.
29228          * @param {Roo.bootstrap.LocationPicker} this
29229          */
29230         loadexception : true
29231     });
29232         
29233 };
29234
29235 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29236     
29237     gMapContext: false,
29238     
29239     latitude: 0,
29240     longitude: 0,
29241     zoom: 15,
29242     mapTypeId: false,
29243     mapTypeControl: false,
29244     disableDoubleClickZoom: false,
29245     scrollwheel: true,
29246     streetViewControl: false,
29247     radius: 0,
29248     locationName: '',
29249     draggable: true,
29250     enableAutocomplete: false,
29251     enableReverseGeocode: true,
29252     markerTitle: '',
29253     
29254     getAutoCreate: function()
29255     {
29256
29257         var cfg = {
29258             tag: 'div',
29259             cls: 'roo-location-picker'
29260         };
29261         
29262         return cfg
29263     },
29264     
29265     initEvents: function(ct, position)
29266     {       
29267         if(!this.el.getWidth() || this.isApplied()){
29268             return;
29269         }
29270         
29271         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29272         
29273         this.initial();
29274     },
29275     
29276     initial: function()
29277     {
29278         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29279             this.fireEvent('loadexception', this);
29280             return;
29281         }
29282         
29283         if(!this.mapTypeId){
29284             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29285         }
29286         
29287         this.gMapContext = this.GMapContext();
29288         
29289         this.initOverlayView();
29290         
29291         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29292         
29293         var _this = this;
29294                 
29295         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29296             _this.setPosition(_this.gMapContext.marker.position);
29297         });
29298         
29299         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29300             _this.fireEvent('mapClick', this, event);
29301             
29302         });
29303
29304         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29305             _this.fireEvent('mapRightClick', this, event);
29306             
29307         });
29308         
29309         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29310             _this.fireEvent('markerClick', this, event);
29311             
29312         });
29313
29314         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29315             _this.fireEvent('markerRightClick', this, event);
29316             
29317         });
29318         
29319         this.setPosition(this.gMapContext.location);
29320         
29321         this.fireEvent('initial', this, this.gMapContext.location);
29322     },
29323     
29324     initOverlayView: function()
29325     {
29326         var _this = this;
29327         
29328         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29329             
29330             draw: function()
29331             {
29332                 _this.fireEvent('OverlayViewDraw', _this);
29333             },
29334             
29335             onAdd: function()
29336             {
29337                 _this.fireEvent('OverlayViewOnAdd', _this);
29338             },
29339             
29340             onRemove: function()
29341             {
29342                 _this.fireEvent('OverlayViewOnRemove', _this);
29343             },
29344             
29345             show: function(cpx)
29346             {
29347                 _this.fireEvent('OverlayViewShow', _this, cpx);
29348             },
29349             
29350             hide: function()
29351             {
29352                 _this.fireEvent('OverlayViewHide', _this);
29353             }
29354             
29355         });
29356     },
29357     
29358     fromLatLngToContainerPixel: function(event)
29359     {
29360         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29361     },
29362     
29363     isApplied: function() 
29364     {
29365         return this.getGmapContext() == false ? false : true;
29366     },
29367     
29368     getGmapContext: function() 
29369     {
29370         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29371     },
29372     
29373     GMapContext: function() 
29374     {
29375         var position = new google.maps.LatLng(this.latitude, this.longitude);
29376         
29377         var _map = new google.maps.Map(this.el.dom, {
29378             center: position,
29379             zoom: this.zoom,
29380             mapTypeId: this.mapTypeId,
29381             mapTypeControl: this.mapTypeControl,
29382             disableDoubleClickZoom: this.disableDoubleClickZoom,
29383             scrollwheel: this.scrollwheel,
29384             streetViewControl: this.streetViewControl,
29385             locationName: this.locationName,
29386             draggable: this.draggable,
29387             enableAutocomplete: this.enableAutocomplete,
29388             enableReverseGeocode: this.enableReverseGeocode
29389         });
29390         
29391         var _marker = new google.maps.Marker({
29392             position: position,
29393             map: _map,
29394             title: this.markerTitle,
29395             draggable: this.draggable
29396         });
29397         
29398         return {
29399             map: _map,
29400             marker: _marker,
29401             circle: null,
29402             location: position,
29403             radius: this.radius,
29404             locationName: this.locationName,
29405             addressComponents: {
29406                 formatted_address: null,
29407                 addressLine1: null,
29408                 addressLine2: null,
29409                 streetName: null,
29410                 streetNumber: null,
29411                 city: null,
29412                 district: null,
29413                 state: null,
29414                 stateOrProvince: null
29415             },
29416             settings: this,
29417             domContainer: this.el.dom,
29418             geodecoder: new google.maps.Geocoder()
29419         };
29420     },
29421     
29422     drawCircle: function(center, radius, options) 
29423     {
29424         if (this.gMapContext.circle != null) {
29425             this.gMapContext.circle.setMap(null);
29426         }
29427         if (radius > 0) {
29428             radius *= 1;
29429             options = Roo.apply({}, options, {
29430                 strokeColor: "#0000FF",
29431                 strokeOpacity: .35,
29432                 strokeWeight: 2,
29433                 fillColor: "#0000FF",
29434                 fillOpacity: .2
29435             });
29436             
29437             options.map = this.gMapContext.map;
29438             options.radius = radius;
29439             options.center = center;
29440             this.gMapContext.circle = new google.maps.Circle(options);
29441             return this.gMapContext.circle;
29442         }
29443         
29444         return null;
29445     },
29446     
29447     setPosition: function(location) 
29448     {
29449         this.gMapContext.location = location;
29450         this.gMapContext.marker.setPosition(location);
29451         this.gMapContext.map.panTo(location);
29452         this.drawCircle(location, this.gMapContext.radius, {});
29453         
29454         var _this = this;
29455         
29456         if (this.gMapContext.settings.enableReverseGeocode) {
29457             this.gMapContext.geodecoder.geocode({
29458                 latLng: this.gMapContext.location
29459             }, function(results, status) {
29460                 
29461                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29462                     _this.gMapContext.locationName = results[0].formatted_address;
29463                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29464                     
29465                     _this.fireEvent('positionchanged', this, location);
29466                 }
29467             });
29468             
29469             return;
29470         }
29471         
29472         this.fireEvent('positionchanged', this, location);
29473     },
29474     
29475     resize: function()
29476     {
29477         google.maps.event.trigger(this.gMapContext.map, "resize");
29478         
29479         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29480         
29481         this.fireEvent('resize', this);
29482     },
29483     
29484     setPositionByLatLng: function(latitude, longitude)
29485     {
29486         this.setPosition(new google.maps.LatLng(latitude, longitude));
29487     },
29488     
29489     getCurrentPosition: function() 
29490     {
29491         return {
29492             latitude: this.gMapContext.location.lat(),
29493             longitude: this.gMapContext.location.lng()
29494         };
29495     },
29496     
29497     getAddressName: function() 
29498     {
29499         return this.gMapContext.locationName;
29500     },
29501     
29502     getAddressComponents: function() 
29503     {
29504         return this.gMapContext.addressComponents;
29505     },
29506     
29507     address_component_from_google_geocode: function(address_components) 
29508     {
29509         var result = {};
29510         
29511         for (var i = 0; i < address_components.length; i++) {
29512             var component = address_components[i];
29513             if (component.types.indexOf("postal_code") >= 0) {
29514                 result.postalCode = component.short_name;
29515             } else if (component.types.indexOf("street_number") >= 0) {
29516                 result.streetNumber = component.short_name;
29517             } else if (component.types.indexOf("route") >= 0) {
29518                 result.streetName = component.short_name;
29519             } else if (component.types.indexOf("neighborhood") >= 0) {
29520                 result.city = component.short_name;
29521             } else if (component.types.indexOf("locality") >= 0) {
29522                 result.city = component.short_name;
29523             } else if (component.types.indexOf("sublocality") >= 0) {
29524                 result.district = component.short_name;
29525             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29526                 result.stateOrProvince = component.short_name;
29527             } else if (component.types.indexOf("country") >= 0) {
29528                 result.country = component.short_name;
29529             }
29530         }
29531         
29532         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29533         result.addressLine2 = "";
29534         return result;
29535     },
29536     
29537     setZoomLevel: function(zoom)
29538     {
29539         this.gMapContext.map.setZoom(zoom);
29540     },
29541     
29542     show: function()
29543     {
29544         if(!this.el){
29545             return;
29546         }
29547         
29548         this.el.show();
29549         
29550         this.resize();
29551         
29552         this.fireEvent('show', this);
29553     },
29554     
29555     hide: function()
29556     {
29557         if(!this.el){
29558             return;
29559         }
29560         
29561         this.el.hide();
29562         
29563         this.fireEvent('hide', this);
29564     }
29565     
29566 });
29567
29568 Roo.apply(Roo.bootstrap.LocationPicker, {
29569     
29570     OverlayView : function(map, options)
29571     {
29572         options = options || {};
29573         
29574         this.setMap(map);
29575     }
29576     
29577     
29578 });/**
29579  * @class Roo.bootstrap.Alert
29580  * @extends Roo.bootstrap.Component
29581  * Bootstrap Alert class - shows an alert area box
29582  * eg
29583  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29584   Enter a valid email address
29585 </div>
29586  * @licence LGPL
29587  * @cfg {String} title The title of alert
29588  * @cfg {String} html The content of alert
29589  * @cfg {String} weight (  success | info | warning | danger )
29590  * @cfg {String} faicon font-awesomeicon
29591  * 
29592  * @constructor
29593  * Create a new alert
29594  * @param {Object} config The config object
29595  */
29596
29597
29598 Roo.bootstrap.Alert = function(config){
29599     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29600     
29601 };
29602
29603 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29604     
29605     title: '',
29606     html: '',
29607     weight: false,
29608     faicon: false,
29609     
29610     getAutoCreate : function()
29611     {
29612         
29613         var cfg = {
29614             tag : 'div',
29615             cls : 'alert',
29616             cn : [
29617                 {
29618                     tag : 'i',
29619                     cls : 'roo-alert-icon'
29620                     
29621                 },
29622                 {
29623                     tag : 'b',
29624                     cls : 'roo-alert-title',
29625                     html : this.title
29626                 },
29627                 {
29628                     tag : 'span',
29629                     cls : 'roo-alert-text',
29630                     html : this.html
29631                 }
29632             ]
29633         };
29634         
29635         if(this.faicon){
29636             cfg.cn[0].cls += ' fa ' + this.faicon;
29637         }
29638         
29639         if(this.weight){
29640             cfg.cls += ' alert-' + this.weight;
29641         }
29642         
29643         return cfg;
29644     },
29645     
29646     initEvents: function() 
29647     {
29648         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29649     },
29650     
29651     setTitle : function(str)
29652     {
29653         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29654     },
29655     
29656     setText : function(str)
29657     {
29658         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29659     },
29660     
29661     setWeight : function(weight)
29662     {
29663         if(this.weight){
29664             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29665         }
29666         
29667         this.weight = weight;
29668         
29669         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29670     },
29671     
29672     setIcon : function(icon)
29673     {
29674         if(this.faicon){
29675             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29676         }
29677         
29678         this.faicon = icon;
29679         
29680         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29681     },
29682     
29683     hide: function() 
29684     {
29685         this.el.hide();   
29686     },
29687     
29688     show: function() 
29689     {  
29690         this.el.show();   
29691     }
29692     
29693 });
29694
29695  
29696 /*
29697 * Licence: LGPL
29698 */
29699
29700 /**
29701  * @class Roo.bootstrap.UploadCropbox
29702  * @extends Roo.bootstrap.Component
29703  * Bootstrap UploadCropbox class
29704  * @cfg {String} emptyText show when image has been loaded
29705  * @cfg {String} rotateNotify show when image too small to rotate
29706  * @cfg {Number} errorTimeout default 3000
29707  * @cfg {Number} minWidth default 300
29708  * @cfg {Number} minHeight default 300
29709  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29710  * @cfg {Boolean} isDocument (true|false) default false
29711  * @cfg {String} url action url
29712  * @cfg {String} paramName default 'imageUpload'
29713  * @cfg {String} method default POST
29714  * @cfg {Boolean} loadMask (true|false) default true
29715  * @cfg {Boolean} loadingText default 'Loading...'
29716  * 
29717  * @constructor
29718  * Create a new UploadCropbox
29719  * @param {Object} config The config object
29720  */
29721
29722 Roo.bootstrap.UploadCropbox = function(config){
29723     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29724     
29725     this.addEvents({
29726         /**
29727          * @event beforeselectfile
29728          * Fire before select file
29729          * @param {Roo.bootstrap.UploadCropbox} this
29730          */
29731         "beforeselectfile" : true,
29732         /**
29733          * @event initial
29734          * Fire after initEvent
29735          * @param {Roo.bootstrap.UploadCropbox} this
29736          */
29737         "initial" : true,
29738         /**
29739          * @event crop
29740          * Fire after initEvent
29741          * @param {Roo.bootstrap.UploadCropbox} this
29742          * @param {String} data
29743          */
29744         "crop" : true,
29745         /**
29746          * @event prepare
29747          * Fire when preparing the file data
29748          * @param {Roo.bootstrap.UploadCropbox} this
29749          * @param {Object} file
29750          */
29751         "prepare" : true,
29752         /**
29753          * @event exception
29754          * Fire when get exception
29755          * @param {Roo.bootstrap.UploadCropbox} this
29756          * @param {XMLHttpRequest} xhr
29757          */
29758         "exception" : true,
29759         /**
29760          * @event beforeloadcanvas
29761          * Fire before load the canvas
29762          * @param {Roo.bootstrap.UploadCropbox} this
29763          * @param {String} src
29764          */
29765         "beforeloadcanvas" : true,
29766         /**
29767          * @event trash
29768          * Fire when trash image
29769          * @param {Roo.bootstrap.UploadCropbox} this
29770          */
29771         "trash" : true,
29772         /**
29773          * @event download
29774          * Fire when download the image
29775          * @param {Roo.bootstrap.UploadCropbox} this
29776          */
29777         "download" : true,
29778         /**
29779          * @event footerbuttonclick
29780          * Fire when footerbuttonclick
29781          * @param {Roo.bootstrap.UploadCropbox} this
29782          * @param {String} type
29783          */
29784         "footerbuttonclick" : true,
29785         /**
29786          * @event resize
29787          * Fire when resize
29788          * @param {Roo.bootstrap.UploadCropbox} this
29789          */
29790         "resize" : true,
29791         /**
29792          * @event rotate
29793          * Fire when rotate the image
29794          * @param {Roo.bootstrap.UploadCropbox} this
29795          * @param {String} pos
29796          */
29797         "rotate" : true,
29798         /**
29799          * @event inspect
29800          * Fire when inspect the file
29801          * @param {Roo.bootstrap.UploadCropbox} this
29802          * @param {Object} file
29803          */
29804         "inspect" : true,
29805         /**
29806          * @event upload
29807          * Fire when xhr upload the file
29808          * @param {Roo.bootstrap.UploadCropbox} this
29809          * @param {Object} data
29810          */
29811         "upload" : true,
29812         /**
29813          * @event arrange
29814          * Fire when arrange the file data
29815          * @param {Roo.bootstrap.UploadCropbox} this
29816          * @param {Object} formData
29817          */
29818         "arrange" : true
29819     });
29820     
29821     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29822 };
29823
29824 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29825     
29826     emptyText : 'Click to upload image',
29827     rotateNotify : 'Image is too small to rotate',
29828     errorTimeout : 3000,
29829     scale : 0,
29830     baseScale : 1,
29831     rotate : 0,
29832     dragable : false,
29833     pinching : false,
29834     mouseX : 0,
29835     mouseY : 0,
29836     cropData : false,
29837     minWidth : 300,
29838     minHeight : 300,
29839     file : false,
29840     exif : {},
29841     baseRotate : 1,
29842     cropType : 'image/jpeg',
29843     buttons : false,
29844     canvasLoaded : false,
29845     isDocument : false,
29846     method : 'POST',
29847     paramName : 'imageUpload',
29848     loadMask : true,
29849     loadingText : 'Loading...',
29850     maskEl : false,
29851     
29852     getAutoCreate : function()
29853     {
29854         var cfg = {
29855             tag : 'div',
29856             cls : 'roo-upload-cropbox',
29857             cn : [
29858                 {
29859                     tag : 'input',
29860                     cls : 'roo-upload-cropbox-selector',
29861                     type : 'file'
29862                 },
29863                 {
29864                     tag : 'div',
29865                     cls : 'roo-upload-cropbox-body',
29866                     style : 'cursor:pointer',
29867                     cn : [
29868                         {
29869                             tag : 'div',
29870                             cls : 'roo-upload-cropbox-preview'
29871                         },
29872                         {
29873                             tag : 'div',
29874                             cls : 'roo-upload-cropbox-thumb'
29875                         },
29876                         {
29877                             tag : 'div',
29878                             cls : 'roo-upload-cropbox-empty-notify',
29879                             html : this.emptyText
29880                         },
29881                         {
29882                             tag : 'div',
29883                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29884                             html : this.rotateNotify
29885                         }
29886                     ]
29887                 },
29888                 {
29889                     tag : 'div',
29890                     cls : 'roo-upload-cropbox-footer',
29891                     cn : {
29892                         tag : 'div',
29893                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29894                         cn : []
29895                     }
29896                 }
29897             ]
29898         };
29899         
29900         return cfg;
29901     },
29902     
29903     onRender : function(ct, position)
29904     {
29905         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29906         
29907         if (this.buttons.length) {
29908             
29909             Roo.each(this.buttons, function(bb) {
29910                 
29911                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29912                 
29913                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29914                 
29915             }, this);
29916         }
29917         
29918         if(this.loadMask){
29919             this.maskEl = this.el;
29920         }
29921     },
29922     
29923     initEvents : function()
29924     {
29925         this.urlAPI = (window.createObjectURL && window) || 
29926                                 (window.URL && URL.revokeObjectURL && URL) || 
29927                                 (window.webkitURL && webkitURL);
29928                         
29929         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29930         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29931         
29932         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29933         this.selectorEl.hide();
29934         
29935         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29936         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29937         
29938         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29939         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29940         this.thumbEl.hide();
29941         
29942         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29943         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29944         
29945         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29946         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29947         this.errorEl.hide();
29948         
29949         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29950         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29951         this.footerEl.hide();
29952         
29953         this.setThumbBoxSize();
29954         
29955         this.bind();
29956         
29957         this.resize();
29958         
29959         this.fireEvent('initial', this);
29960     },
29961
29962     bind : function()
29963     {
29964         var _this = this;
29965         
29966         window.addEventListener("resize", function() { _this.resize(); } );
29967         
29968         this.bodyEl.on('click', this.beforeSelectFile, this);
29969         
29970         if(Roo.isTouch){
29971             this.bodyEl.on('touchstart', this.onTouchStart, this);
29972             this.bodyEl.on('touchmove', this.onTouchMove, this);
29973             this.bodyEl.on('touchend', this.onTouchEnd, this);
29974         }
29975         
29976         if(!Roo.isTouch){
29977             this.bodyEl.on('mousedown', this.onMouseDown, this);
29978             this.bodyEl.on('mousemove', this.onMouseMove, this);
29979             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29980             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29981             Roo.get(document).on('mouseup', this.onMouseUp, this);
29982         }
29983         
29984         this.selectorEl.on('change', this.onFileSelected, this);
29985     },
29986     
29987     reset : function()
29988     {    
29989         this.scale = 0;
29990         this.baseScale = 1;
29991         this.rotate = 0;
29992         this.baseRotate = 1;
29993         this.dragable = false;
29994         this.pinching = false;
29995         this.mouseX = 0;
29996         this.mouseY = 0;
29997         this.cropData = false;
29998         this.notifyEl.dom.innerHTML = this.emptyText;
29999         
30000         this.selectorEl.dom.value = '';
30001         
30002     },
30003     
30004     resize : function()
30005     {
30006         if(this.fireEvent('resize', this) != false){
30007             this.setThumbBoxPosition();
30008             this.setCanvasPosition();
30009         }
30010     },
30011     
30012     onFooterButtonClick : function(e, el, o, type)
30013     {
30014         switch (type) {
30015             case 'rotate-left' :
30016                 this.onRotateLeft(e);
30017                 break;
30018             case 'rotate-right' :
30019                 this.onRotateRight(e);
30020                 break;
30021             case 'picture' :
30022                 this.beforeSelectFile(e);
30023                 break;
30024             case 'trash' :
30025                 this.trash(e);
30026                 break;
30027             case 'crop' :
30028                 this.crop(e);
30029                 break;
30030             case 'download' :
30031                 this.download(e);
30032                 break;
30033             default :
30034                 break;
30035         }
30036         
30037         this.fireEvent('footerbuttonclick', this, type);
30038     },
30039     
30040     beforeSelectFile : function(e)
30041     {
30042         e.preventDefault();
30043         
30044         if(this.fireEvent('beforeselectfile', this) != false){
30045             this.selectorEl.dom.click();
30046         }
30047     },
30048     
30049     onFileSelected : function(e)
30050     {
30051         e.preventDefault();
30052         
30053         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30054             return;
30055         }
30056         
30057         var file = this.selectorEl.dom.files[0];
30058         
30059         if(this.fireEvent('inspect', this, file) != false){
30060             this.prepare(file);
30061         }
30062         
30063     },
30064     
30065     trash : function(e)
30066     {
30067         this.fireEvent('trash', this);
30068     },
30069     
30070     download : function(e)
30071     {
30072         this.fireEvent('download', this);
30073     },
30074     
30075     loadCanvas : function(src)
30076     {   
30077         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30078             
30079             this.reset();
30080             
30081             this.imageEl = document.createElement('img');
30082             
30083             var _this = this;
30084             
30085             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30086             
30087             this.imageEl.src = src;
30088         }
30089     },
30090     
30091     onLoadCanvas : function()
30092     {   
30093         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30094         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30095         
30096         this.bodyEl.un('click', this.beforeSelectFile, this);
30097         
30098         this.notifyEl.hide();
30099         this.thumbEl.show();
30100         this.footerEl.show();
30101         
30102         this.baseRotateLevel();
30103         
30104         if(this.isDocument){
30105             this.setThumbBoxSize();
30106         }
30107         
30108         this.setThumbBoxPosition();
30109         
30110         this.baseScaleLevel();
30111         
30112         this.draw();
30113         
30114         this.resize();
30115         
30116         this.canvasLoaded = true;
30117         
30118         if(this.loadMask){
30119             this.maskEl.unmask();
30120         }
30121         
30122     },
30123     
30124     setCanvasPosition : function()
30125     {   
30126         if(!this.canvasEl){
30127             return;
30128         }
30129         
30130         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30131         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30132         
30133         this.previewEl.setLeft(pw);
30134         this.previewEl.setTop(ph);
30135         
30136     },
30137     
30138     onMouseDown : function(e)
30139     {   
30140         e.stopEvent();
30141         
30142         this.dragable = true;
30143         this.pinching = false;
30144         
30145         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30146             this.dragable = false;
30147             return;
30148         }
30149         
30150         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30151         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30152         
30153     },
30154     
30155     onMouseMove : function(e)
30156     {   
30157         e.stopEvent();
30158         
30159         if(!this.canvasLoaded){
30160             return;
30161         }
30162         
30163         if (!this.dragable){
30164             return;
30165         }
30166         
30167         var minX = Math.ceil(this.thumbEl.getLeft(true));
30168         var minY = Math.ceil(this.thumbEl.getTop(true));
30169         
30170         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30171         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30172         
30173         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30174         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30175         
30176         x = x - this.mouseX;
30177         y = y - this.mouseY;
30178         
30179         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30180         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30181         
30182         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30183         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30184         
30185         this.previewEl.setLeft(bgX);
30186         this.previewEl.setTop(bgY);
30187         
30188         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30189         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30190     },
30191     
30192     onMouseUp : function(e)
30193     {   
30194         e.stopEvent();
30195         
30196         this.dragable = false;
30197     },
30198     
30199     onMouseWheel : function(e)
30200     {   
30201         e.stopEvent();
30202         
30203         this.startScale = this.scale;
30204         
30205         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30206         
30207         if(!this.zoomable()){
30208             this.scale = this.startScale;
30209             return;
30210         }
30211         
30212         this.draw();
30213         
30214         return;
30215     },
30216     
30217     zoomable : function()
30218     {
30219         var minScale = this.thumbEl.getWidth() / this.minWidth;
30220         
30221         if(this.minWidth < this.minHeight){
30222             minScale = this.thumbEl.getHeight() / this.minHeight;
30223         }
30224         
30225         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30226         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30227         
30228         if(
30229                 this.isDocument &&
30230                 (this.rotate == 0 || this.rotate == 180) && 
30231                 (
30232                     width > this.imageEl.OriginWidth || 
30233                     height > this.imageEl.OriginHeight ||
30234                     (width < this.minWidth && height < this.minHeight)
30235                 )
30236         ){
30237             return false;
30238         }
30239         
30240         if(
30241                 this.isDocument &&
30242                 (this.rotate == 90 || this.rotate == 270) && 
30243                 (
30244                     width > this.imageEl.OriginWidth || 
30245                     height > this.imageEl.OriginHeight ||
30246                     (width < this.minHeight && height < this.minWidth)
30247                 )
30248         ){
30249             return false;
30250         }
30251         
30252         if(
30253                 !this.isDocument &&
30254                 (this.rotate == 0 || this.rotate == 180) && 
30255                 (
30256                     width < this.minWidth || 
30257                     width > this.imageEl.OriginWidth || 
30258                     height < this.minHeight || 
30259                     height > this.imageEl.OriginHeight
30260                 )
30261         ){
30262             return false;
30263         }
30264         
30265         if(
30266                 !this.isDocument &&
30267                 (this.rotate == 90 || this.rotate == 270) && 
30268                 (
30269                     width < this.minHeight || 
30270                     width > this.imageEl.OriginWidth || 
30271                     height < this.minWidth || 
30272                     height > this.imageEl.OriginHeight
30273                 )
30274         ){
30275             return false;
30276         }
30277         
30278         return true;
30279         
30280     },
30281     
30282     onRotateLeft : function(e)
30283     {   
30284         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30285             
30286             var minScale = this.thumbEl.getWidth() / this.minWidth;
30287             
30288             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30289             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30290             
30291             this.startScale = this.scale;
30292             
30293             while (this.getScaleLevel() < minScale){
30294             
30295                 this.scale = this.scale + 1;
30296                 
30297                 if(!this.zoomable()){
30298                     break;
30299                 }
30300                 
30301                 if(
30302                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30303                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30304                 ){
30305                     continue;
30306                 }
30307                 
30308                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30309
30310                 this.draw();
30311                 
30312                 return;
30313             }
30314             
30315             this.scale = this.startScale;
30316             
30317             this.onRotateFail();
30318             
30319             return false;
30320         }
30321         
30322         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30323
30324         if(this.isDocument){
30325             this.setThumbBoxSize();
30326             this.setThumbBoxPosition();
30327             this.setCanvasPosition();
30328         }
30329         
30330         this.draw();
30331         
30332         this.fireEvent('rotate', this, 'left');
30333         
30334     },
30335     
30336     onRotateRight : function(e)
30337     {
30338         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30339             
30340             var minScale = this.thumbEl.getWidth() / this.minWidth;
30341         
30342             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30343             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30344             
30345             this.startScale = this.scale;
30346             
30347             while (this.getScaleLevel() < minScale){
30348             
30349                 this.scale = this.scale + 1;
30350                 
30351                 if(!this.zoomable()){
30352                     break;
30353                 }
30354                 
30355                 if(
30356                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30357                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30358                 ){
30359                     continue;
30360                 }
30361                 
30362                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30363
30364                 this.draw();
30365                 
30366                 return;
30367             }
30368             
30369             this.scale = this.startScale;
30370             
30371             this.onRotateFail();
30372             
30373             return false;
30374         }
30375         
30376         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30377
30378         if(this.isDocument){
30379             this.setThumbBoxSize();
30380             this.setThumbBoxPosition();
30381             this.setCanvasPosition();
30382         }
30383         
30384         this.draw();
30385         
30386         this.fireEvent('rotate', this, 'right');
30387     },
30388     
30389     onRotateFail : function()
30390     {
30391         this.errorEl.show(true);
30392         
30393         var _this = this;
30394         
30395         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30396     },
30397     
30398     draw : function()
30399     {
30400         this.previewEl.dom.innerHTML = '';
30401         
30402         var canvasEl = document.createElement("canvas");
30403         
30404         var contextEl = canvasEl.getContext("2d");
30405         
30406         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30407         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30408         var center = this.imageEl.OriginWidth / 2;
30409         
30410         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30411             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30413             center = this.imageEl.OriginHeight / 2;
30414         }
30415         
30416         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30417         
30418         contextEl.translate(center, center);
30419         contextEl.rotate(this.rotate * Math.PI / 180);
30420
30421         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30422         
30423         this.canvasEl = document.createElement("canvas");
30424         
30425         this.contextEl = this.canvasEl.getContext("2d");
30426         
30427         switch (this.rotate) {
30428             case 0 :
30429                 
30430                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30431                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30432                 
30433                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30434                 
30435                 break;
30436             case 90 : 
30437                 
30438                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30439                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30440                 
30441                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30442                     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);
30443                     break;
30444                 }
30445                 
30446                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30447                 
30448                 break;
30449             case 180 :
30450                 
30451                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30452                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30453                 
30454                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30455                     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);
30456                     break;
30457                 }
30458                 
30459                 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);
30460                 
30461                 break;
30462             case 270 :
30463                 
30464                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30465                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30466         
30467                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30468                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30469                     break;
30470                 }
30471                 
30472                 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);
30473                 
30474                 break;
30475             default : 
30476                 break;
30477         }
30478         
30479         this.previewEl.appendChild(this.canvasEl);
30480         
30481         this.setCanvasPosition();
30482     },
30483     
30484     crop : function()
30485     {
30486         if(!this.canvasLoaded){
30487             return;
30488         }
30489         
30490         var imageCanvas = document.createElement("canvas");
30491         
30492         var imageContext = imageCanvas.getContext("2d");
30493         
30494         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30495         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30496         
30497         var center = imageCanvas.width / 2;
30498         
30499         imageContext.translate(center, center);
30500         
30501         imageContext.rotate(this.rotate * Math.PI / 180);
30502         
30503         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30504         
30505         var canvas = document.createElement("canvas");
30506         
30507         var context = canvas.getContext("2d");
30508                 
30509         canvas.width = this.minWidth;
30510         canvas.height = this.minHeight;
30511
30512         switch (this.rotate) {
30513             case 0 :
30514                 
30515                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30516                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30517                 
30518                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30519                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30520                 
30521                 var targetWidth = this.minWidth - 2 * x;
30522                 var targetHeight = this.minHeight - 2 * y;
30523                 
30524                 var scale = 1;
30525                 
30526                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30527                     scale = targetWidth / width;
30528                 }
30529                 
30530                 if(x > 0 && y == 0){
30531                     scale = targetHeight / height;
30532                 }
30533                 
30534                 if(x > 0 && y > 0){
30535                     scale = targetWidth / width;
30536                     
30537                     if(width < height){
30538                         scale = targetHeight / height;
30539                     }
30540                 }
30541                 
30542                 context.scale(scale, scale);
30543                 
30544                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30545                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30546
30547                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30548                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30549
30550                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30551                 
30552                 break;
30553             case 90 : 
30554                 
30555                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30556                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30557                 
30558                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30559                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30560                 
30561                 var targetWidth = this.minWidth - 2 * x;
30562                 var targetHeight = this.minHeight - 2 * y;
30563                 
30564                 var scale = 1;
30565                 
30566                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30567                     scale = targetWidth / width;
30568                 }
30569                 
30570                 if(x > 0 && y == 0){
30571                     scale = targetHeight / height;
30572                 }
30573                 
30574                 if(x > 0 && y > 0){
30575                     scale = targetWidth / width;
30576                     
30577                     if(width < height){
30578                         scale = targetHeight / height;
30579                     }
30580                 }
30581                 
30582                 context.scale(scale, scale);
30583                 
30584                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30585                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30586
30587                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30588                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30589                 
30590                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30591                 
30592                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30593                 
30594                 break;
30595             case 180 :
30596                 
30597                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30598                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30599                 
30600                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30601                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30602                 
30603                 var targetWidth = this.minWidth - 2 * x;
30604                 var targetHeight = this.minHeight - 2 * y;
30605                 
30606                 var scale = 1;
30607                 
30608                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30609                     scale = targetWidth / width;
30610                 }
30611                 
30612                 if(x > 0 && y == 0){
30613                     scale = targetHeight / height;
30614                 }
30615                 
30616                 if(x > 0 && y > 0){
30617                     scale = targetWidth / width;
30618                     
30619                     if(width < height){
30620                         scale = targetHeight / height;
30621                     }
30622                 }
30623                 
30624                 context.scale(scale, scale);
30625                 
30626                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30627                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30628
30629                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30630                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30631
30632                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30633                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30634                 
30635                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30636                 
30637                 break;
30638             case 270 :
30639                 
30640                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30641                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30642                 
30643                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30644                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30645                 
30646                 var targetWidth = this.minWidth - 2 * x;
30647                 var targetHeight = this.minHeight - 2 * y;
30648                 
30649                 var scale = 1;
30650                 
30651                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30652                     scale = targetWidth / width;
30653                 }
30654                 
30655                 if(x > 0 && y == 0){
30656                     scale = targetHeight / height;
30657                 }
30658                 
30659                 if(x > 0 && y > 0){
30660                     scale = targetWidth / width;
30661                     
30662                     if(width < height){
30663                         scale = targetHeight / height;
30664                     }
30665                 }
30666                 
30667                 context.scale(scale, scale);
30668                 
30669                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30670                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30671
30672                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30673                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30674                 
30675                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30676                 
30677                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30678                 
30679                 break;
30680             default : 
30681                 break;
30682         }
30683         
30684         this.cropData = canvas.toDataURL(this.cropType);
30685         
30686         if(this.fireEvent('crop', this, this.cropData) !== false){
30687             this.process(this.file, this.cropData);
30688         }
30689         
30690         return;
30691         
30692     },
30693     
30694     setThumbBoxSize : function()
30695     {
30696         var width, height;
30697         
30698         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30699             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30700             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30701             
30702             this.minWidth = width;
30703             this.minHeight = height;
30704             
30705             if(this.rotate == 90 || this.rotate == 270){
30706                 this.minWidth = height;
30707                 this.minHeight = width;
30708             }
30709         }
30710         
30711         height = 300;
30712         width = Math.ceil(this.minWidth * height / this.minHeight);
30713         
30714         if(this.minWidth > this.minHeight){
30715             width = 300;
30716             height = Math.ceil(this.minHeight * width / this.minWidth);
30717         }
30718         
30719         this.thumbEl.setStyle({
30720             width : width + 'px',
30721             height : height + 'px'
30722         });
30723
30724         return;
30725             
30726     },
30727     
30728     setThumbBoxPosition : function()
30729     {
30730         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30731         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30732         
30733         this.thumbEl.setLeft(x);
30734         this.thumbEl.setTop(y);
30735         
30736     },
30737     
30738     baseRotateLevel : function()
30739     {
30740         this.baseRotate = 1;
30741         
30742         if(
30743                 typeof(this.exif) != 'undefined' &&
30744                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30745                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30746         ){
30747             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30748         }
30749         
30750         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30751         
30752     },
30753     
30754     baseScaleLevel : function()
30755     {
30756         var width, height;
30757         
30758         if(this.isDocument){
30759             
30760             if(this.baseRotate == 6 || this.baseRotate == 8){
30761             
30762                 height = this.thumbEl.getHeight();
30763                 this.baseScale = height / this.imageEl.OriginWidth;
30764
30765                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30766                     width = this.thumbEl.getWidth();
30767                     this.baseScale = width / this.imageEl.OriginHeight;
30768                 }
30769
30770                 return;
30771             }
30772
30773             height = this.thumbEl.getHeight();
30774             this.baseScale = height / this.imageEl.OriginHeight;
30775
30776             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30777                 width = this.thumbEl.getWidth();
30778                 this.baseScale = width / this.imageEl.OriginWidth;
30779             }
30780
30781             return;
30782         }
30783         
30784         if(this.baseRotate == 6 || this.baseRotate == 8){
30785             
30786             width = this.thumbEl.getHeight();
30787             this.baseScale = width / this.imageEl.OriginHeight;
30788             
30789             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30790                 height = this.thumbEl.getWidth();
30791                 this.baseScale = height / this.imageEl.OriginHeight;
30792             }
30793             
30794             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30795                 height = this.thumbEl.getWidth();
30796                 this.baseScale = height / this.imageEl.OriginHeight;
30797                 
30798                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30799                     width = this.thumbEl.getHeight();
30800                     this.baseScale = width / this.imageEl.OriginWidth;
30801                 }
30802             }
30803             
30804             return;
30805         }
30806         
30807         width = this.thumbEl.getWidth();
30808         this.baseScale = width / this.imageEl.OriginWidth;
30809         
30810         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30811             height = this.thumbEl.getHeight();
30812             this.baseScale = height / this.imageEl.OriginHeight;
30813         }
30814         
30815         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30816             
30817             height = this.thumbEl.getHeight();
30818             this.baseScale = height / this.imageEl.OriginHeight;
30819             
30820             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30821                 width = this.thumbEl.getWidth();
30822                 this.baseScale = width / this.imageEl.OriginWidth;
30823             }
30824             
30825         }
30826         
30827         return;
30828     },
30829     
30830     getScaleLevel : function()
30831     {
30832         return this.baseScale * Math.pow(1.1, this.scale);
30833     },
30834     
30835     onTouchStart : function(e)
30836     {
30837         if(!this.canvasLoaded){
30838             this.beforeSelectFile(e);
30839             return;
30840         }
30841         
30842         var touches = e.browserEvent.touches;
30843         
30844         if(!touches){
30845             return;
30846         }
30847         
30848         if(touches.length == 1){
30849             this.onMouseDown(e);
30850             return;
30851         }
30852         
30853         if(touches.length != 2){
30854             return;
30855         }
30856         
30857         var coords = [];
30858         
30859         for(var i = 0, finger; finger = touches[i]; i++){
30860             coords.push(finger.pageX, finger.pageY);
30861         }
30862         
30863         var x = Math.pow(coords[0] - coords[2], 2);
30864         var y = Math.pow(coords[1] - coords[3], 2);
30865         
30866         this.startDistance = Math.sqrt(x + y);
30867         
30868         this.startScale = this.scale;
30869         
30870         this.pinching = true;
30871         this.dragable = false;
30872         
30873     },
30874     
30875     onTouchMove : function(e)
30876     {
30877         if(!this.pinching && !this.dragable){
30878             return;
30879         }
30880         
30881         var touches = e.browserEvent.touches;
30882         
30883         if(!touches){
30884             return;
30885         }
30886         
30887         if(this.dragable){
30888             this.onMouseMove(e);
30889             return;
30890         }
30891         
30892         var coords = [];
30893         
30894         for(var i = 0, finger; finger = touches[i]; i++){
30895             coords.push(finger.pageX, finger.pageY);
30896         }
30897         
30898         var x = Math.pow(coords[0] - coords[2], 2);
30899         var y = Math.pow(coords[1] - coords[3], 2);
30900         
30901         this.endDistance = Math.sqrt(x + y);
30902         
30903         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30904         
30905         if(!this.zoomable()){
30906             this.scale = this.startScale;
30907             return;
30908         }
30909         
30910         this.draw();
30911         
30912     },
30913     
30914     onTouchEnd : function(e)
30915     {
30916         this.pinching = false;
30917         this.dragable = false;
30918         
30919     },
30920     
30921     process : function(file, crop)
30922     {
30923         if(this.loadMask){
30924             this.maskEl.mask(this.loadingText);
30925         }
30926         
30927         this.xhr = new XMLHttpRequest();
30928         
30929         file.xhr = this.xhr;
30930
30931         this.xhr.open(this.method, this.url, true);
30932         
30933         var headers = {
30934             "Accept": "application/json",
30935             "Cache-Control": "no-cache",
30936             "X-Requested-With": "XMLHttpRequest"
30937         };
30938         
30939         for (var headerName in headers) {
30940             var headerValue = headers[headerName];
30941             if (headerValue) {
30942                 this.xhr.setRequestHeader(headerName, headerValue);
30943             }
30944         }
30945         
30946         var _this = this;
30947         
30948         this.xhr.onload = function()
30949         {
30950             _this.xhrOnLoad(_this.xhr);
30951         }
30952         
30953         this.xhr.onerror = function()
30954         {
30955             _this.xhrOnError(_this.xhr);
30956         }
30957         
30958         var formData = new FormData();
30959
30960         formData.append('returnHTML', 'NO');
30961         
30962         if(crop){
30963             formData.append('crop', crop);
30964         }
30965         
30966         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30967             formData.append(this.paramName, file, file.name);
30968         }
30969         
30970         if(typeof(file.filename) != 'undefined'){
30971             formData.append('filename', file.filename);
30972         }
30973         
30974         if(typeof(file.mimetype) != 'undefined'){
30975             formData.append('mimetype', file.mimetype);
30976         }
30977         
30978         if(this.fireEvent('arrange', this, formData) != false){
30979             this.xhr.send(formData);
30980         };
30981     },
30982     
30983     xhrOnLoad : function(xhr)
30984     {
30985         if(this.loadMask){
30986             this.maskEl.unmask();
30987         }
30988         
30989         if (xhr.readyState !== 4) {
30990             this.fireEvent('exception', this, xhr);
30991             return;
30992         }
30993
30994         var response = Roo.decode(xhr.responseText);
30995         
30996         if(!response.success){
30997             this.fireEvent('exception', this, xhr);
30998             return;
30999         }
31000         
31001         var response = Roo.decode(xhr.responseText);
31002         
31003         this.fireEvent('upload', this, response);
31004         
31005     },
31006     
31007     xhrOnError : function()
31008     {
31009         if(this.loadMask){
31010             this.maskEl.unmask();
31011         }
31012         
31013         Roo.log('xhr on error');
31014         
31015         var response = Roo.decode(xhr.responseText);
31016           
31017         Roo.log(response);
31018         
31019     },
31020     
31021     prepare : function(file)
31022     {   
31023         if(this.loadMask){
31024             this.maskEl.mask(this.loadingText);
31025         }
31026         
31027         this.file = false;
31028         this.exif = {};
31029         
31030         if(typeof(file) === 'string'){
31031             this.loadCanvas(file);
31032             return;
31033         }
31034         
31035         if(!file || !this.urlAPI){
31036             return;
31037         }
31038         
31039         this.file = file;
31040         this.cropType = file.type;
31041         
31042         var _this = this;
31043         
31044         if(this.fireEvent('prepare', this, this.file) != false){
31045             
31046             var reader = new FileReader();
31047             
31048             reader.onload = function (e) {
31049                 if (e.target.error) {
31050                     Roo.log(e.target.error);
31051                     return;
31052                 }
31053                 
31054                 var buffer = e.target.result,
31055                     dataView = new DataView(buffer),
31056                     offset = 2,
31057                     maxOffset = dataView.byteLength - 4,
31058                     markerBytes,
31059                     markerLength;
31060                 
31061                 if (dataView.getUint16(0) === 0xffd8) {
31062                     while (offset < maxOffset) {
31063                         markerBytes = dataView.getUint16(offset);
31064                         
31065                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31066                             markerLength = dataView.getUint16(offset + 2) + 2;
31067                             if (offset + markerLength > dataView.byteLength) {
31068                                 Roo.log('Invalid meta data: Invalid segment size.');
31069                                 break;
31070                             }
31071                             
31072                             if(markerBytes == 0xffe1){
31073                                 _this.parseExifData(
31074                                     dataView,
31075                                     offset,
31076                                     markerLength
31077                                 );
31078                             }
31079                             
31080                             offset += markerLength;
31081                             
31082                             continue;
31083                         }
31084                         
31085                         break;
31086                     }
31087                     
31088                 }
31089                 
31090                 var url = _this.urlAPI.createObjectURL(_this.file);
31091                 
31092                 _this.loadCanvas(url);
31093                 
31094                 return;
31095             }
31096             
31097             reader.readAsArrayBuffer(this.file);
31098             
31099         }
31100         
31101     },
31102     
31103     parseExifData : function(dataView, offset, length)
31104     {
31105         var tiffOffset = offset + 10,
31106             littleEndian,
31107             dirOffset;
31108     
31109         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31110             // No Exif data, might be XMP data instead
31111             return;
31112         }
31113         
31114         // Check for the ASCII code for "Exif" (0x45786966):
31115         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31116             // No Exif data, might be XMP data instead
31117             return;
31118         }
31119         if (tiffOffset + 8 > dataView.byteLength) {
31120             Roo.log('Invalid Exif data: Invalid segment size.');
31121             return;
31122         }
31123         // Check for the two null bytes:
31124         if (dataView.getUint16(offset + 8) !== 0x0000) {
31125             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31126             return;
31127         }
31128         // Check the byte alignment:
31129         switch (dataView.getUint16(tiffOffset)) {
31130         case 0x4949:
31131             littleEndian = true;
31132             break;
31133         case 0x4D4D:
31134             littleEndian = false;
31135             break;
31136         default:
31137             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31138             return;
31139         }
31140         // Check for the TIFF tag marker (0x002A):
31141         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31142             Roo.log('Invalid Exif data: Missing TIFF marker.');
31143             return;
31144         }
31145         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31146         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31147         
31148         this.parseExifTags(
31149             dataView,
31150             tiffOffset,
31151             tiffOffset + dirOffset,
31152             littleEndian
31153         );
31154     },
31155     
31156     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31157     {
31158         var tagsNumber,
31159             dirEndOffset,
31160             i;
31161         if (dirOffset + 6 > dataView.byteLength) {
31162             Roo.log('Invalid Exif data: Invalid directory offset.');
31163             return;
31164         }
31165         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31166         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31167         if (dirEndOffset + 4 > dataView.byteLength) {
31168             Roo.log('Invalid Exif data: Invalid directory size.');
31169             return;
31170         }
31171         for (i = 0; i < tagsNumber; i += 1) {
31172             this.parseExifTag(
31173                 dataView,
31174                 tiffOffset,
31175                 dirOffset + 2 + 12 * i, // tag offset
31176                 littleEndian
31177             );
31178         }
31179         // Return the offset to the next directory:
31180         return dataView.getUint32(dirEndOffset, littleEndian);
31181     },
31182     
31183     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31184     {
31185         var tag = dataView.getUint16(offset, littleEndian);
31186         
31187         this.exif[tag] = this.getExifValue(
31188             dataView,
31189             tiffOffset,
31190             offset,
31191             dataView.getUint16(offset + 2, littleEndian), // tag type
31192             dataView.getUint32(offset + 4, littleEndian), // tag length
31193             littleEndian
31194         );
31195     },
31196     
31197     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31198     {
31199         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31200             tagSize,
31201             dataOffset,
31202             values,
31203             i,
31204             str,
31205             c;
31206     
31207         if (!tagType) {
31208             Roo.log('Invalid Exif data: Invalid tag type.');
31209             return;
31210         }
31211         
31212         tagSize = tagType.size * length;
31213         // Determine if the value is contained in the dataOffset bytes,
31214         // or if the value at the dataOffset is a pointer to the actual data:
31215         dataOffset = tagSize > 4 ?
31216                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31217         if (dataOffset + tagSize > dataView.byteLength) {
31218             Roo.log('Invalid Exif data: Invalid data offset.');
31219             return;
31220         }
31221         if (length === 1) {
31222             return tagType.getValue(dataView, dataOffset, littleEndian);
31223         }
31224         values = [];
31225         for (i = 0; i < length; i += 1) {
31226             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31227         }
31228         
31229         if (tagType.ascii) {
31230             str = '';
31231             // Concatenate the chars:
31232             for (i = 0; i < values.length; i += 1) {
31233                 c = values[i];
31234                 // Ignore the terminating NULL byte(s):
31235                 if (c === '\u0000') {
31236                     break;
31237                 }
31238                 str += c;
31239             }
31240             return str;
31241         }
31242         return values;
31243     }
31244     
31245 });
31246
31247 Roo.apply(Roo.bootstrap.UploadCropbox, {
31248     tags : {
31249         'Orientation': 0x0112
31250     },
31251     
31252     Orientation: {
31253             1: 0, //'top-left',
31254 //            2: 'top-right',
31255             3: 180, //'bottom-right',
31256 //            4: 'bottom-left',
31257 //            5: 'left-top',
31258             6: 90, //'right-top',
31259 //            7: 'right-bottom',
31260             8: 270 //'left-bottom'
31261     },
31262     
31263     exifTagTypes : {
31264         // byte, 8-bit unsigned int:
31265         1: {
31266             getValue: function (dataView, dataOffset) {
31267                 return dataView.getUint8(dataOffset);
31268             },
31269             size: 1
31270         },
31271         // ascii, 8-bit byte:
31272         2: {
31273             getValue: function (dataView, dataOffset) {
31274                 return String.fromCharCode(dataView.getUint8(dataOffset));
31275             },
31276             size: 1,
31277             ascii: true
31278         },
31279         // short, 16 bit int:
31280         3: {
31281             getValue: function (dataView, dataOffset, littleEndian) {
31282                 return dataView.getUint16(dataOffset, littleEndian);
31283             },
31284             size: 2
31285         },
31286         // long, 32 bit int:
31287         4: {
31288             getValue: function (dataView, dataOffset, littleEndian) {
31289                 return dataView.getUint32(dataOffset, littleEndian);
31290             },
31291             size: 4
31292         },
31293         // rational = two long values, first is numerator, second is denominator:
31294         5: {
31295             getValue: function (dataView, dataOffset, littleEndian) {
31296                 return dataView.getUint32(dataOffset, littleEndian) /
31297                     dataView.getUint32(dataOffset + 4, littleEndian);
31298             },
31299             size: 8
31300         },
31301         // slong, 32 bit signed int:
31302         9: {
31303             getValue: function (dataView, dataOffset, littleEndian) {
31304                 return dataView.getInt32(dataOffset, littleEndian);
31305             },
31306             size: 4
31307         },
31308         // srational, two slongs, first is numerator, second is denominator:
31309         10: {
31310             getValue: function (dataView, dataOffset, littleEndian) {
31311                 return dataView.getInt32(dataOffset, littleEndian) /
31312                     dataView.getInt32(dataOffset + 4, littleEndian);
31313             },
31314             size: 8
31315         }
31316     },
31317     
31318     footer : {
31319         STANDARD : [
31320             {
31321                 tag : 'div',
31322                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31323                 action : 'rotate-left',
31324                 cn : [
31325                     {
31326                         tag : 'button',
31327                         cls : 'btn btn-default',
31328                         html : '<i class="fa fa-undo"></i>'
31329                     }
31330                 ]
31331             },
31332             {
31333                 tag : 'div',
31334                 cls : 'btn-group roo-upload-cropbox-picture',
31335                 action : 'picture',
31336                 cn : [
31337                     {
31338                         tag : 'button',
31339                         cls : 'btn btn-default',
31340                         html : '<i class="fa fa-picture-o"></i>'
31341                     }
31342                 ]
31343             },
31344             {
31345                 tag : 'div',
31346                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31347                 action : 'rotate-right',
31348                 cn : [
31349                     {
31350                         tag : 'button',
31351                         cls : 'btn btn-default',
31352                         html : '<i class="fa fa-repeat"></i>'
31353                     }
31354                 ]
31355             }
31356         ],
31357         DOCUMENT : [
31358             {
31359                 tag : 'div',
31360                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31361                 action : 'rotate-left',
31362                 cn : [
31363                     {
31364                         tag : 'button',
31365                         cls : 'btn btn-default',
31366                         html : '<i class="fa fa-undo"></i>'
31367                     }
31368                 ]
31369             },
31370             {
31371                 tag : 'div',
31372                 cls : 'btn-group roo-upload-cropbox-download',
31373                 action : 'download',
31374                 cn : [
31375                     {
31376                         tag : 'button',
31377                         cls : 'btn btn-default',
31378                         html : '<i class="fa fa-download"></i>'
31379                     }
31380                 ]
31381             },
31382             {
31383                 tag : 'div',
31384                 cls : 'btn-group roo-upload-cropbox-crop',
31385                 action : 'crop',
31386                 cn : [
31387                     {
31388                         tag : 'button',
31389                         cls : 'btn btn-default',
31390                         html : '<i class="fa fa-crop"></i>'
31391                     }
31392                 ]
31393             },
31394             {
31395                 tag : 'div',
31396                 cls : 'btn-group roo-upload-cropbox-trash',
31397                 action : 'trash',
31398                 cn : [
31399                     {
31400                         tag : 'button',
31401                         cls : 'btn btn-default',
31402                         html : '<i class="fa fa-trash"></i>'
31403                     }
31404                 ]
31405             },
31406             {
31407                 tag : 'div',
31408                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31409                 action : 'rotate-right',
31410                 cn : [
31411                     {
31412                         tag : 'button',
31413                         cls : 'btn btn-default',
31414                         html : '<i class="fa fa-repeat"></i>'
31415                     }
31416                 ]
31417             }
31418         ],
31419         ROTATOR : [
31420             {
31421                 tag : 'div',
31422                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31423                 action : 'rotate-left',
31424                 cn : [
31425                     {
31426                         tag : 'button',
31427                         cls : 'btn btn-default',
31428                         html : '<i class="fa fa-undo"></i>'
31429                     }
31430                 ]
31431             },
31432             {
31433                 tag : 'div',
31434                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31435                 action : 'rotate-right',
31436                 cn : [
31437                     {
31438                         tag : 'button',
31439                         cls : 'btn btn-default',
31440                         html : '<i class="fa fa-repeat"></i>'
31441                     }
31442                 ]
31443             }
31444         ]
31445     }
31446 });
31447
31448 /*
31449 * Licence: LGPL
31450 */
31451
31452 /**
31453  * @class Roo.bootstrap.DocumentManager
31454  * @extends Roo.bootstrap.Component
31455  * Bootstrap DocumentManager class
31456  * @cfg {String} paramName default 'imageUpload'
31457  * @cfg {String} toolTipName default 'filename'
31458  * @cfg {String} method default POST
31459  * @cfg {String} url action url
31460  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31461  * @cfg {Boolean} multiple multiple upload default true
31462  * @cfg {Number} thumbSize default 300
31463  * @cfg {String} fieldLabel
31464  * @cfg {Number} labelWidth default 4
31465  * @cfg {String} labelAlign (left|top) default left
31466  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31467 * @cfg {Number} labellg set the width of label (1-12)
31468  * @cfg {Number} labelmd set the width of label (1-12)
31469  * @cfg {Number} labelsm set the width of label (1-12)
31470  * @cfg {Number} labelxs set the width of label (1-12)
31471  * 
31472  * @constructor
31473  * Create a new DocumentManager
31474  * @param {Object} config The config object
31475  */
31476
31477 Roo.bootstrap.DocumentManager = function(config){
31478     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31479     
31480     this.files = [];
31481     this.delegates = [];
31482     
31483     this.addEvents({
31484         /**
31485          * @event initial
31486          * Fire when initial the DocumentManager
31487          * @param {Roo.bootstrap.DocumentManager} this
31488          */
31489         "initial" : true,
31490         /**
31491          * @event inspect
31492          * inspect selected file
31493          * @param {Roo.bootstrap.DocumentManager} this
31494          * @param {File} file
31495          */
31496         "inspect" : true,
31497         /**
31498          * @event exception
31499          * Fire when xhr load exception
31500          * @param {Roo.bootstrap.DocumentManager} this
31501          * @param {XMLHttpRequest} xhr
31502          */
31503         "exception" : true,
31504         /**
31505          * @event afterupload
31506          * Fire when xhr load exception
31507          * @param {Roo.bootstrap.DocumentManager} this
31508          * @param {XMLHttpRequest} xhr
31509          */
31510         "afterupload" : true,
31511         /**
31512          * @event prepare
31513          * prepare the form data
31514          * @param {Roo.bootstrap.DocumentManager} this
31515          * @param {Object} formData
31516          */
31517         "prepare" : true,
31518         /**
31519          * @event remove
31520          * Fire when remove the file
31521          * @param {Roo.bootstrap.DocumentManager} this
31522          * @param {Object} file
31523          */
31524         "remove" : true,
31525         /**
31526          * @event refresh
31527          * Fire after refresh the file
31528          * @param {Roo.bootstrap.DocumentManager} this
31529          */
31530         "refresh" : true,
31531         /**
31532          * @event click
31533          * Fire after click the image
31534          * @param {Roo.bootstrap.DocumentManager} this
31535          * @param {Object} file
31536          */
31537         "click" : true,
31538         /**
31539          * @event edit
31540          * Fire when upload a image and editable set to true
31541          * @param {Roo.bootstrap.DocumentManager} this
31542          * @param {Object} file
31543          */
31544         "edit" : true,
31545         /**
31546          * @event beforeselectfile
31547          * Fire before select file
31548          * @param {Roo.bootstrap.DocumentManager} this
31549          */
31550         "beforeselectfile" : true,
31551         /**
31552          * @event process
31553          * Fire before process file
31554          * @param {Roo.bootstrap.DocumentManager} this
31555          * @param {Object} file
31556          */
31557         "process" : true,
31558         /**
31559          * @event previewrendered
31560          * Fire when preview rendered
31561          * @param {Roo.bootstrap.DocumentManager} this
31562          * @param {Object} file
31563          */
31564         "previewrendered" : true,
31565         /**
31566          */
31567         "previewResize" : true
31568         
31569     });
31570 };
31571
31572 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31573     
31574     boxes : 0,
31575     inputName : '',
31576     thumbSize : 300,
31577     multiple : true,
31578     files : false,
31579     method : 'POST',
31580     url : '',
31581     paramName : 'imageUpload',
31582     toolTipName : 'filename',
31583     fieldLabel : '',
31584     labelWidth : 4,
31585     labelAlign : 'left',
31586     editable : true,
31587     delegates : false,
31588     xhr : false, 
31589     
31590     labellg : 0,
31591     labelmd : 0,
31592     labelsm : 0,
31593     labelxs : 0,
31594     
31595     getAutoCreate : function()
31596     {   
31597         var managerWidget = {
31598             tag : 'div',
31599             cls : 'roo-document-manager',
31600             cn : [
31601                 {
31602                     tag : 'input',
31603                     cls : 'roo-document-manager-selector',
31604                     type : 'file'
31605                 },
31606                 {
31607                     tag : 'div',
31608                     cls : 'roo-document-manager-uploader',
31609                     cn : [
31610                         {
31611                             tag : 'div',
31612                             cls : 'roo-document-manager-upload-btn',
31613                             html : '<i class="fa fa-plus"></i>'
31614                         }
31615                     ]
31616                     
31617                 }
31618             ]
31619         };
31620         
31621         var content = [
31622             {
31623                 tag : 'div',
31624                 cls : 'column col-md-12',
31625                 cn : managerWidget
31626             }
31627         ];
31628         
31629         if(this.fieldLabel.length){
31630             
31631             content = [
31632                 {
31633                     tag : 'div',
31634                     cls : 'column col-md-12',
31635                     html : this.fieldLabel
31636                 },
31637                 {
31638                     tag : 'div',
31639                     cls : 'column col-md-12',
31640                     cn : managerWidget
31641                 }
31642             ];
31643
31644             if(this.labelAlign == 'left'){
31645                 content = [
31646                     {
31647                         tag : 'div',
31648                         cls : 'column',
31649                         html : this.fieldLabel
31650                     },
31651                     {
31652                         tag : 'div',
31653                         cls : 'column',
31654                         cn : managerWidget
31655                     }
31656                 ];
31657                 
31658                 if(this.labelWidth > 12){
31659                     content[0].style = "width: " + this.labelWidth + 'px';
31660                 }
31661
31662                 if(this.labelWidth < 13 && this.labelmd == 0){
31663                     this.labelmd = this.labelWidth;
31664                 }
31665
31666                 if(this.labellg > 0){
31667                     content[0].cls += ' col-lg-' + this.labellg;
31668                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31669                 }
31670
31671                 if(this.labelmd > 0){
31672                     content[0].cls += ' col-md-' + this.labelmd;
31673                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31674                 }
31675
31676                 if(this.labelsm > 0){
31677                     content[0].cls += ' col-sm-' + this.labelsm;
31678                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31679                 }
31680
31681                 if(this.labelxs > 0){
31682                     content[0].cls += ' col-xs-' + this.labelxs;
31683                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31684                 }
31685                 
31686             }
31687         }
31688         
31689         var cfg = {
31690             tag : 'div',
31691             cls : 'row clearfix',
31692             cn : content
31693         };
31694         
31695         return cfg;
31696         
31697     },
31698     
31699     initEvents : function()
31700     {
31701         this.managerEl = this.el.select('.roo-document-manager', true).first();
31702         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31703         
31704         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31705         this.selectorEl.hide();
31706         
31707         if(this.multiple){
31708             this.selectorEl.attr('multiple', 'multiple');
31709         }
31710         
31711         this.selectorEl.on('change', this.onFileSelected, this);
31712         
31713         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31714         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31715         
31716         this.uploader.on('click', this.onUploaderClick, this);
31717         
31718         this.renderProgressDialog();
31719         
31720         var _this = this;
31721         
31722         window.addEventListener("resize", function() { _this.refresh(); } );
31723         
31724         this.fireEvent('initial', this);
31725     },
31726     
31727     renderProgressDialog : function()
31728     {
31729         var _this = this;
31730         
31731         this.progressDialog = new Roo.bootstrap.Modal({
31732             cls : 'roo-document-manager-progress-dialog',
31733             allow_close : false,
31734             animate : false,
31735             title : '',
31736             buttons : [
31737                 {
31738                     name  :'cancel',
31739                     weight : 'danger',
31740                     html : 'Cancel'
31741                 }
31742             ], 
31743             listeners : { 
31744                 btnclick : function() {
31745                     _this.uploadCancel();
31746                     this.hide();
31747                 }
31748             }
31749         });
31750          
31751         this.progressDialog.render(Roo.get(document.body));
31752          
31753         this.progress = new Roo.bootstrap.Progress({
31754             cls : 'roo-document-manager-progress',
31755             active : true,
31756             striped : true
31757         });
31758         
31759         this.progress.render(this.progressDialog.getChildContainer());
31760         
31761         this.progressBar = new Roo.bootstrap.ProgressBar({
31762             cls : 'roo-document-manager-progress-bar',
31763             aria_valuenow : 0,
31764             aria_valuemin : 0,
31765             aria_valuemax : 12,
31766             panel : 'success'
31767         });
31768         
31769         this.progressBar.render(this.progress.getChildContainer());
31770     },
31771     
31772     onUploaderClick : function(e)
31773     {
31774         e.preventDefault();
31775      
31776         if(this.fireEvent('beforeselectfile', this) != false){
31777             this.selectorEl.dom.click();
31778         }
31779         
31780     },
31781     
31782     onFileSelected : function(e)
31783     {
31784         e.preventDefault();
31785         
31786         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31787             return;
31788         }
31789         
31790         Roo.each(this.selectorEl.dom.files, function(file){
31791             if(this.fireEvent('inspect', this, file) != false){
31792                 this.files.push(file);
31793             }
31794         }, this);
31795         
31796         this.queue();
31797         
31798     },
31799     
31800     queue : function()
31801     {
31802         this.selectorEl.dom.value = '';
31803         
31804         if(!this.files || !this.files.length){
31805             return;
31806         }
31807         
31808         if(this.boxes > 0 && this.files.length > this.boxes){
31809             this.files = this.files.slice(0, this.boxes);
31810         }
31811         
31812         this.uploader.show();
31813         
31814         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31815             this.uploader.hide();
31816         }
31817         
31818         var _this = this;
31819         
31820         var files = [];
31821         
31822         var docs = [];
31823         
31824         Roo.each(this.files, function(file){
31825             
31826             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31827                 var f = this.renderPreview(file);
31828                 files.push(f);
31829                 return;
31830             }
31831             
31832             if(file.type.indexOf('image') != -1){
31833                 this.delegates.push(
31834                     (function(){
31835                         _this.process(file);
31836                     }).createDelegate(this)
31837                 );
31838         
31839                 return;
31840             }
31841             
31842             docs.push(
31843                 (function(){
31844                     _this.process(file);
31845                 }).createDelegate(this)
31846             );
31847             
31848         }, this);
31849         
31850         this.files = files;
31851         
31852         this.delegates = this.delegates.concat(docs);
31853         
31854         if(!this.delegates.length){
31855             this.refresh();
31856             return;
31857         }
31858         
31859         this.progressBar.aria_valuemax = this.delegates.length;
31860         
31861         this.arrange();
31862         
31863         return;
31864     },
31865     
31866     arrange : function()
31867     {
31868         if(!this.delegates.length){
31869             this.progressDialog.hide();
31870             this.refresh();
31871             return;
31872         }
31873         
31874         var delegate = this.delegates.shift();
31875         
31876         this.progressDialog.show();
31877         
31878         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31879         
31880         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31881         
31882         delegate();
31883     },
31884     
31885     refresh : function()
31886     {
31887         this.uploader.show();
31888         
31889         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31890             this.uploader.hide();
31891         }
31892         
31893         Roo.isTouch ? this.closable(false) : this.closable(true);
31894         
31895         this.fireEvent('refresh', this);
31896     },
31897     
31898     onRemove : function(e, el, o)
31899     {
31900         e.preventDefault();
31901         
31902         this.fireEvent('remove', this, o);
31903         
31904     },
31905     
31906     remove : function(o)
31907     {
31908         var files = [];
31909         
31910         Roo.each(this.files, function(file){
31911             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31912                 files.push(file);
31913                 return;
31914             }
31915
31916             o.target.remove();
31917
31918         }, this);
31919         
31920         this.files = files;
31921         
31922         this.refresh();
31923     },
31924     
31925     clear : function()
31926     {
31927         Roo.each(this.files, function(file){
31928             if(!file.target){
31929                 return;
31930             }
31931             
31932             file.target.remove();
31933
31934         }, this);
31935         
31936         this.files = [];
31937         
31938         this.refresh();
31939     },
31940     
31941     onClick : function(e, el, o)
31942     {
31943         e.preventDefault();
31944         
31945         this.fireEvent('click', this, o);
31946         
31947     },
31948     
31949     closable : function(closable)
31950     {
31951         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31952             
31953             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31954             
31955             if(closable){
31956                 el.show();
31957                 return;
31958             }
31959             
31960             el.hide();
31961             
31962         }, this);
31963     },
31964     
31965     xhrOnLoad : function(xhr)
31966     {
31967         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31968             el.remove();
31969         }, this);
31970         
31971         if (xhr.readyState !== 4) {
31972             this.arrange();
31973             this.fireEvent('exception', this, xhr);
31974             return;
31975         }
31976
31977         var response = Roo.decode(xhr.responseText);
31978         
31979         if(!response.success){
31980             this.arrange();
31981             this.fireEvent('exception', this, xhr);
31982             return;
31983         }
31984         
31985         var file = this.renderPreview(response.data);
31986         
31987         this.files.push(file);
31988         
31989         this.arrange();
31990         
31991         this.fireEvent('afterupload', this, xhr);
31992         
31993     },
31994     
31995     xhrOnError : function(xhr)
31996     {
31997         Roo.log('xhr on error');
31998         
31999         var response = Roo.decode(xhr.responseText);
32000           
32001         Roo.log(response);
32002         
32003         this.arrange();
32004     },
32005     
32006     process : function(file)
32007     {
32008         if(this.fireEvent('process', this, file) !== false){
32009             if(this.editable && file.type.indexOf('image') != -1){
32010                 this.fireEvent('edit', this, file);
32011                 return;
32012             }
32013
32014             this.uploadStart(file, false);
32015
32016             return;
32017         }
32018         
32019     },
32020     
32021     uploadStart : function(file, crop)
32022     {
32023         this.xhr = new XMLHttpRequest();
32024         
32025         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32026             this.arrange();
32027             return;
32028         }
32029         
32030         file.xhr = this.xhr;
32031             
32032         this.managerEl.createChild({
32033             tag : 'div',
32034             cls : 'roo-document-manager-loading',
32035             cn : [
32036                 {
32037                     tag : 'div',
32038                     tooltip : file.name,
32039                     cls : 'roo-document-manager-thumb',
32040                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32041                 }
32042             ]
32043
32044         });
32045
32046         this.xhr.open(this.method, this.url, true);
32047         
32048         var headers = {
32049             "Accept": "application/json",
32050             "Cache-Control": "no-cache",
32051             "X-Requested-With": "XMLHttpRequest"
32052         };
32053         
32054         for (var headerName in headers) {
32055             var headerValue = headers[headerName];
32056             if (headerValue) {
32057                 this.xhr.setRequestHeader(headerName, headerValue);
32058             }
32059         }
32060         
32061         var _this = this;
32062         
32063         this.xhr.onload = function()
32064         {
32065             _this.xhrOnLoad(_this.xhr);
32066         }
32067         
32068         this.xhr.onerror = function()
32069         {
32070             _this.xhrOnError(_this.xhr);
32071         }
32072         
32073         var formData = new FormData();
32074
32075         formData.append('returnHTML', 'NO');
32076         
32077         if(crop){
32078             formData.append('crop', crop);
32079         }
32080         
32081         formData.append(this.paramName, file, file.name);
32082         
32083         var options = {
32084             file : file, 
32085             manually : false
32086         };
32087         
32088         if(this.fireEvent('prepare', this, formData, options) != false){
32089             
32090             if(options.manually){
32091                 return;
32092             }
32093             
32094             this.xhr.send(formData);
32095             return;
32096         };
32097         
32098         this.uploadCancel();
32099     },
32100     
32101     uploadCancel : function()
32102     {
32103         if (this.xhr) {
32104             this.xhr.abort();
32105         }
32106         
32107         this.delegates = [];
32108         
32109         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32110             el.remove();
32111         }, this);
32112         
32113         this.arrange();
32114     },
32115     
32116     renderPreview : function(file)
32117     {
32118         if(typeof(file.target) != 'undefined' && file.target){
32119             return file;
32120         }
32121         
32122         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32123         
32124         var previewEl = this.managerEl.createChild({
32125             tag : 'div',
32126             cls : 'roo-document-manager-preview',
32127             cn : [
32128                 {
32129                     tag : 'div',
32130                     tooltip : file[this.toolTipName],
32131                     cls : 'roo-document-manager-thumb',
32132                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32133                 },
32134                 {
32135                     tag : 'button',
32136                     cls : 'close',
32137                     html : '<i class="fa fa-times-circle"></i>'
32138                 }
32139             ]
32140         });
32141
32142         var close = previewEl.select('button.close', true).first();
32143
32144         close.on('click', this.onRemove, this, file);
32145
32146         file.target = previewEl;
32147
32148         var image = previewEl.select('img', true).first();
32149         
32150         var _this = this;
32151         
32152         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32153         
32154         image.on('click', this.onClick, this, file);
32155         
32156         this.fireEvent('previewrendered', this, file);
32157         
32158         return file;
32159         
32160     },
32161     
32162     onPreviewLoad : function(file, image)
32163     {
32164         if(typeof(file.target) == 'undefined' || !file.target){
32165             return;
32166         }
32167         
32168         var width = image.dom.naturalWidth || image.dom.width;
32169         var height = image.dom.naturalHeight || image.dom.height;
32170         
32171         if(!this.previewResize) {
32172             return;
32173         }
32174         
32175         if(width > height){
32176             file.target.addClass('wide');
32177             return;
32178         }
32179         
32180         file.target.addClass('tall');
32181         return;
32182         
32183     },
32184     
32185     uploadFromSource : function(file, crop)
32186     {
32187         this.xhr = new XMLHttpRequest();
32188         
32189         this.managerEl.createChild({
32190             tag : 'div',
32191             cls : 'roo-document-manager-loading',
32192             cn : [
32193                 {
32194                     tag : 'div',
32195                     tooltip : file.name,
32196                     cls : 'roo-document-manager-thumb',
32197                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32198                 }
32199             ]
32200
32201         });
32202
32203         this.xhr.open(this.method, this.url, true);
32204         
32205         var headers = {
32206             "Accept": "application/json",
32207             "Cache-Control": "no-cache",
32208             "X-Requested-With": "XMLHttpRequest"
32209         };
32210         
32211         for (var headerName in headers) {
32212             var headerValue = headers[headerName];
32213             if (headerValue) {
32214                 this.xhr.setRequestHeader(headerName, headerValue);
32215             }
32216         }
32217         
32218         var _this = this;
32219         
32220         this.xhr.onload = function()
32221         {
32222             _this.xhrOnLoad(_this.xhr);
32223         }
32224         
32225         this.xhr.onerror = function()
32226         {
32227             _this.xhrOnError(_this.xhr);
32228         }
32229         
32230         var formData = new FormData();
32231
32232         formData.append('returnHTML', 'NO');
32233         
32234         formData.append('crop', crop);
32235         
32236         if(typeof(file.filename) != 'undefined'){
32237             formData.append('filename', file.filename);
32238         }
32239         
32240         if(typeof(file.mimetype) != 'undefined'){
32241             formData.append('mimetype', file.mimetype);
32242         }
32243         
32244         Roo.log(formData);
32245         
32246         if(this.fireEvent('prepare', this, formData) != false){
32247             this.xhr.send(formData);
32248         };
32249     }
32250 });
32251
32252 /*
32253 * Licence: LGPL
32254 */
32255
32256 /**
32257  * @class Roo.bootstrap.DocumentViewer
32258  * @extends Roo.bootstrap.Component
32259  * Bootstrap DocumentViewer class
32260  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32261  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32262  * 
32263  * @constructor
32264  * Create a new DocumentViewer
32265  * @param {Object} config The config object
32266  */
32267
32268 Roo.bootstrap.DocumentViewer = function(config){
32269     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32270     
32271     this.addEvents({
32272         /**
32273          * @event initial
32274          * Fire after initEvent
32275          * @param {Roo.bootstrap.DocumentViewer} this
32276          */
32277         "initial" : true,
32278         /**
32279          * @event click
32280          * Fire after click
32281          * @param {Roo.bootstrap.DocumentViewer} this
32282          */
32283         "click" : true,
32284         /**
32285          * @event download
32286          * Fire after download button
32287          * @param {Roo.bootstrap.DocumentViewer} this
32288          */
32289         "download" : true,
32290         /**
32291          * @event trash
32292          * Fire after trash button
32293          * @param {Roo.bootstrap.DocumentViewer} this
32294          */
32295         "trash" : true
32296         
32297     });
32298 };
32299
32300 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32301     
32302     showDownload : true,
32303     
32304     showTrash : true,
32305     
32306     getAutoCreate : function()
32307     {
32308         var cfg = {
32309             tag : 'div',
32310             cls : 'roo-document-viewer',
32311             cn : [
32312                 {
32313                     tag : 'div',
32314                     cls : 'roo-document-viewer-body',
32315                     cn : [
32316                         {
32317                             tag : 'div',
32318                             cls : 'roo-document-viewer-thumb',
32319                             cn : [
32320                                 {
32321                                     tag : 'img',
32322                                     cls : 'roo-document-viewer-image'
32323                                 }
32324                             ]
32325                         }
32326                     ]
32327                 },
32328                 {
32329                     tag : 'div',
32330                     cls : 'roo-document-viewer-footer',
32331                     cn : {
32332                         tag : 'div',
32333                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32334                         cn : [
32335                             {
32336                                 tag : 'div',
32337                                 cls : 'btn-group roo-document-viewer-download',
32338                                 cn : [
32339                                     {
32340                                         tag : 'button',
32341                                         cls : 'btn btn-default',
32342                                         html : '<i class="fa fa-download"></i>'
32343                                     }
32344                                 ]
32345                             },
32346                             {
32347                                 tag : 'div',
32348                                 cls : 'btn-group roo-document-viewer-trash',
32349                                 cn : [
32350                                     {
32351                                         tag : 'button',
32352                                         cls : 'btn btn-default',
32353                                         html : '<i class="fa fa-trash"></i>'
32354                                     }
32355                                 ]
32356                             }
32357                         ]
32358                     }
32359                 }
32360             ]
32361         };
32362         
32363         return cfg;
32364     },
32365     
32366     initEvents : function()
32367     {
32368         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32369         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32370         
32371         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32372         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32373         
32374         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32375         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32376         
32377         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32378         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32379         
32380         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32381         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32382         
32383         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32384         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32385         
32386         this.bodyEl.on('click', this.onClick, this);
32387         this.downloadBtn.on('click', this.onDownload, this);
32388         this.trashBtn.on('click', this.onTrash, this);
32389         
32390         this.downloadBtn.hide();
32391         this.trashBtn.hide();
32392         
32393         if(this.showDownload){
32394             this.downloadBtn.show();
32395         }
32396         
32397         if(this.showTrash){
32398             this.trashBtn.show();
32399         }
32400         
32401         if(!this.showDownload && !this.showTrash) {
32402             this.footerEl.hide();
32403         }
32404         
32405     },
32406     
32407     initial : function()
32408     {
32409         this.fireEvent('initial', this);
32410         
32411     },
32412     
32413     onClick : function(e)
32414     {
32415         e.preventDefault();
32416         
32417         this.fireEvent('click', this);
32418     },
32419     
32420     onDownload : function(e)
32421     {
32422         e.preventDefault();
32423         
32424         this.fireEvent('download', this);
32425     },
32426     
32427     onTrash : function(e)
32428     {
32429         e.preventDefault();
32430         
32431         this.fireEvent('trash', this);
32432     }
32433     
32434 });
32435 /*
32436  * - LGPL
32437  *
32438  * nav progress bar
32439  * 
32440  */
32441
32442 /**
32443  * @class Roo.bootstrap.NavProgressBar
32444  * @extends Roo.bootstrap.Component
32445  * Bootstrap NavProgressBar class
32446  * 
32447  * @constructor
32448  * Create a new nav progress bar
32449  * @param {Object} config The config object
32450  */
32451
32452 Roo.bootstrap.NavProgressBar = function(config){
32453     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32454
32455     this.bullets = this.bullets || [];
32456    
32457 //    Roo.bootstrap.NavProgressBar.register(this);
32458      this.addEvents({
32459         /**
32460              * @event changed
32461              * Fires when the active item changes
32462              * @param {Roo.bootstrap.NavProgressBar} this
32463              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32464              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32465          */
32466         'changed': true
32467      });
32468     
32469 };
32470
32471 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32472     
32473     bullets : [],
32474     barItems : [],
32475     
32476     getAutoCreate : function()
32477     {
32478         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32479         
32480         cfg = {
32481             tag : 'div',
32482             cls : 'roo-navigation-bar-group',
32483             cn : [
32484                 {
32485                     tag : 'div',
32486                     cls : 'roo-navigation-top-bar'
32487                 },
32488                 {
32489                     tag : 'div',
32490                     cls : 'roo-navigation-bullets-bar',
32491                     cn : [
32492                         {
32493                             tag : 'ul',
32494                             cls : 'roo-navigation-bar'
32495                         }
32496                     ]
32497                 },
32498                 
32499                 {
32500                     tag : 'div',
32501                     cls : 'roo-navigation-bottom-bar'
32502                 }
32503             ]
32504             
32505         };
32506         
32507         return cfg;
32508         
32509     },
32510     
32511     initEvents: function() 
32512     {
32513         
32514     },
32515     
32516     onRender : function(ct, position) 
32517     {
32518         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32519         
32520         if(this.bullets.length){
32521             Roo.each(this.bullets, function(b){
32522                this.addItem(b);
32523             }, this);
32524         }
32525         
32526         this.format();
32527         
32528     },
32529     
32530     addItem : function(cfg)
32531     {
32532         var item = new Roo.bootstrap.NavProgressItem(cfg);
32533         
32534         item.parentId = this.id;
32535         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32536         
32537         if(cfg.html){
32538             var top = new Roo.bootstrap.Element({
32539                 tag : 'div',
32540                 cls : 'roo-navigation-bar-text'
32541             });
32542             
32543             var bottom = new Roo.bootstrap.Element({
32544                 tag : 'div',
32545                 cls : 'roo-navigation-bar-text'
32546             });
32547             
32548             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32549             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32550             
32551             var topText = new Roo.bootstrap.Element({
32552                 tag : 'span',
32553                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32554             });
32555             
32556             var bottomText = new Roo.bootstrap.Element({
32557                 tag : 'span',
32558                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32559             });
32560             
32561             topText.onRender(top.el, null);
32562             bottomText.onRender(bottom.el, null);
32563             
32564             item.topEl = top;
32565             item.bottomEl = bottom;
32566         }
32567         
32568         this.barItems.push(item);
32569         
32570         return item;
32571     },
32572     
32573     getActive : function()
32574     {
32575         var active = false;
32576         
32577         Roo.each(this.barItems, function(v){
32578             
32579             if (!v.isActive()) {
32580                 return;
32581             }
32582             
32583             active = v;
32584             return false;
32585             
32586         });
32587         
32588         return active;
32589     },
32590     
32591     setActiveItem : function(item)
32592     {
32593         var prev = false;
32594         
32595         Roo.each(this.barItems, function(v){
32596             if (v.rid == item.rid) {
32597                 return ;
32598             }
32599             
32600             if (v.isActive()) {
32601                 v.setActive(false);
32602                 prev = v;
32603             }
32604         });
32605
32606         item.setActive(true);
32607         
32608         this.fireEvent('changed', this, item, prev);
32609     },
32610     
32611     getBarItem: function(rid)
32612     {
32613         var ret = false;
32614         
32615         Roo.each(this.barItems, function(e) {
32616             if (e.rid != rid) {
32617                 return;
32618             }
32619             
32620             ret =  e;
32621             return false;
32622         });
32623         
32624         return ret;
32625     },
32626     
32627     indexOfItem : function(item)
32628     {
32629         var index = false;
32630         
32631         Roo.each(this.barItems, function(v, i){
32632             
32633             if (v.rid != item.rid) {
32634                 return;
32635             }
32636             
32637             index = i;
32638             return false
32639         });
32640         
32641         return index;
32642     },
32643     
32644     setActiveNext : function()
32645     {
32646         var i = this.indexOfItem(this.getActive());
32647         
32648         if (i > this.barItems.length) {
32649             return;
32650         }
32651         
32652         this.setActiveItem(this.barItems[i+1]);
32653     },
32654     
32655     setActivePrev : function()
32656     {
32657         var i = this.indexOfItem(this.getActive());
32658         
32659         if (i  < 1) {
32660             return;
32661         }
32662         
32663         this.setActiveItem(this.barItems[i-1]);
32664     },
32665     
32666     format : function()
32667     {
32668         if(!this.barItems.length){
32669             return;
32670         }
32671      
32672         var width = 100 / this.barItems.length;
32673         
32674         Roo.each(this.barItems, function(i){
32675             i.el.setStyle('width', width + '%');
32676             i.topEl.el.setStyle('width', width + '%');
32677             i.bottomEl.el.setStyle('width', width + '%');
32678         }, this);
32679         
32680     }
32681     
32682 });
32683 /*
32684  * - LGPL
32685  *
32686  * Nav Progress Item
32687  * 
32688  */
32689
32690 /**
32691  * @class Roo.bootstrap.NavProgressItem
32692  * @extends Roo.bootstrap.Component
32693  * Bootstrap NavProgressItem class
32694  * @cfg {String} rid the reference id
32695  * @cfg {Boolean} active (true|false) Is item active default false
32696  * @cfg {Boolean} disabled (true|false) Is item active default false
32697  * @cfg {String} html
32698  * @cfg {String} position (top|bottom) text position default bottom
32699  * @cfg {String} icon show icon instead of number
32700  * 
32701  * @constructor
32702  * Create a new NavProgressItem
32703  * @param {Object} config The config object
32704  */
32705 Roo.bootstrap.NavProgressItem = function(config){
32706     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32707     this.addEvents({
32708         // raw events
32709         /**
32710          * @event click
32711          * The raw click event for the entire grid.
32712          * @param {Roo.bootstrap.NavProgressItem} this
32713          * @param {Roo.EventObject} e
32714          */
32715         "click" : true
32716     });
32717    
32718 };
32719
32720 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32721     
32722     rid : '',
32723     active : false,
32724     disabled : false,
32725     html : '',
32726     position : 'bottom',
32727     icon : false,
32728     
32729     getAutoCreate : function()
32730     {
32731         var iconCls = 'roo-navigation-bar-item-icon';
32732         
32733         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32734         
32735         var cfg = {
32736             tag: 'li',
32737             cls: 'roo-navigation-bar-item',
32738             cn : [
32739                 {
32740                     tag : 'i',
32741                     cls : iconCls
32742                 }
32743             ]
32744         };
32745         
32746         if(this.active){
32747             cfg.cls += ' active';
32748         }
32749         if(this.disabled){
32750             cfg.cls += ' disabled';
32751         }
32752         
32753         return cfg;
32754     },
32755     
32756     disable : function()
32757     {
32758         this.setDisabled(true);
32759     },
32760     
32761     enable : function()
32762     {
32763         this.setDisabled(false);
32764     },
32765     
32766     initEvents: function() 
32767     {
32768         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32769         
32770         this.iconEl.on('click', this.onClick, this);
32771     },
32772     
32773     onClick : function(e)
32774     {
32775         e.preventDefault();
32776         
32777         if(this.disabled){
32778             return;
32779         }
32780         
32781         if(this.fireEvent('click', this, e) === false){
32782             return;
32783         };
32784         
32785         this.parent().setActiveItem(this);
32786     },
32787     
32788     isActive: function () 
32789     {
32790         return this.active;
32791     },
32792     
32793     setActive : function(state)
32794     {
32795         if(this.active == state){
32796             return;
32797         }
32798         
32799         this.active = state;
32800         
32801         if (state) {
32802             this.el.addClass('active');
32803             return;
32804         }
32805         
32806         this.el.removeClass('active');
32807         
32808         return;
32809     },
32810     
32811     setDisabled : function(state)
32812     {
32813         if(this.disabled == state){
32814             return;
32815         }
32816         
32817         this.disabled = state;
32818         
32819         if (state) {
32820             this.el.addClass('disabled');
32821             return;
32822         }
32823         
32824         this.el.removeClass('disabled');
32825     },
32826     
32827     tooltipEl : function()
32828     {
32829         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32830     }
32831 });
32832  
32833
32834  /*
32835  * - LGPL
32836  *
32837  * FieldLabel
32838  * 
32839  */
32840
32841 /**
32842  * @class Roo.bootstrap.FieldLabel
32843  * @extends Roo.bootstrap.Component
32844  * Bootstrap FieldLabel class
32845  * @cfg {String} html contents of the element
32846  * @cfg {String} tag tag of the element default label
32847  * @cfg {String} cls class of the element
32848  * @cfg {String} target label target 
32849  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32850  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32851  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32852  * @cfg {String} iconTooltip default "This field is required"
32853  * @cfg {String} indicatorpos (left|right) default left
32854  * 
32855  * @constructor
32856  * Create a new FieldLabel
32857  * @param {Object} config The config object
32858  */
32859
32860 Roo.bootstrap.FieldLabel = function(config){
32861     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32862     
32863     this.addEvents({
32864             /**
32865              * @event invalid
32866              * Fires after the field has been marked as invalid.
32867              * @param {Roo.form.FieldLabel} this
32868              * @param {String} msg The validation message
32869              */
32870             invalid : true,
32871             /**
32872              * @event valid
32873              * Fires after the field has been validated with no errors.
32874              * @param {Roo.form.FieldLabel} this
32875              */
32876             valid : true
32877         });
32878 };
32879
32880 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32881     
32882     tag: 'label',
32883     cls: '',
32884     html: '',
32885     target: '',
32886     allowBlank : true,
32887     invalidClass : 'has-warning',
32888     validClass : 'has-success',
32889     iconTooltip : 'This field is required',
32890     indicatorpos : 'left',
32891     
32892     getAutoCreate : function(){
32893         
32894         var cls = "";
32895         if (!this.allowBlank) {
32896             cls  = "visible";
32897         }
32898         
32899         var cfg = {
32900             tag : this.tag,
32901             cls : 'roo-bootstrap-field-label ' + this.cls,
32902             for : this.target,
32903             cn : [
32904                 {
32905                     tag : 'i',
32906                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32907                     tooltip : this.iconTooltip
32908                 },
32909                 {
32910                     tag : 'span',
32911                     html : this.html
32912                 }
32913             ] 
32914         };
32915         
32916         if(this.indicatorpos == 'right'){
32917             var cfg = {
32918                 tag : this.tag,
32919                 cls : 'roo-bootstrap-field-label ' + this.cls,
32920                 for : this.target,
32921                 cn : [
32922                     {
32923                         tag : 'span',
32924                         html : this.html
32925                     },
32926                     {
32927                         tag : 'i',
32928                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32929                         tooltip : this.iconTooltip
32930                     }
32931                 ] 
32932             };
32933         }
32934         
32935         return cfg;
32936     },
32937     
32938     initEvents: function() 
32939     {
32940         Roo.bootstrap.Element.superclass.initEvents.call(this);
32941         
32942         this.indicator = this.indicatorEl();
32943         
32944         if(this.indicator){
32945             this.indicator.removeClass('visible');
32946             this.indicator.addClass('invisible');
32947         }
32948         
32949         Roo.bootstrap.FieldLabel.register(this);
32950     },
32951     
32952     indicatorEl : function()
32953     {
32954         var indicator = this.el.select('i.roo-required-indicator',true).first();
32955         
32956         if(!indicator){
32957             return false;
32958         }
32959         
32960         return indicator;
32961         
32962     },
32963     
32964     /**
32965      * Mark this field as valid
32966      */
32967     markValid : function()
32968     {
32969         if(this.indicator){
32970             this.indicator.removeClass('visible');
32971             this.indicator.addClass('invisible');
32972         }
32973         if (Roo.bootstrap.version == 3) {
32974             this.el.removeClass(this.invalidClass);
32975             this.el.addClass(this.validClass);
32976         } else {
32977             this.el.removeClass('is-invalid');
32978             this.el.addClass('is-valid');
32979         }
32980         
32981         
32982         this.fireEvent('valid', this);
32983     },
32984     
32985     /**
32986      * Mark this field as invalid
32987      * @param {String} msg The validation message
32988      */
32989     markInvalid : function(msg)
32990     {
32991         if(this.indicator){
32992             this.indicator.removeClass('invisible');
32993             this.indicator.addClass('visible');
32994         }
32995           if (Roo.bootstrap.version == 3) {
32996             this.el.removeClass(this.validClass);
32997             this.el.addClass(this.invalidClass);
32998         } else {
32999             this.el.removeClass('is-valid');
33000             this.el.addClass('is-invalid');
33001         }
33002         
33003         
33004         this.fireEvent('invalid', this, msg);
33005     }
33006     
33007    
33008 });
33009
33010 Roo.apply(Roo.bootstrap.FieldLabel, {
33011     
33012     groups: {},
33013     
33014      /**
33015     * register a FieldLabel Group
33016     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33017     */
33018     register : function(label)
33019     {
33020         if(this.groups.hasOwnProperty(label.target)){
33021             return;
33022         }
33023      
33024         this.groups[label.target] = label;
33025         
33026     },
33027     /**
33028     * fetch a FieldLabel Group based on the target
33029     * @param {string} target
33030     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33031     */
33032     get: function(target) {
33033         if (typeof(this.groups[target]) == 'undefined') {
33034             return false;
33035         }
33036         
33037         return this.groups[target] ;
33038     }
33039 });
33040
33041  
33042
33043  /*
33044  * - LGPL
33045  *
33046  * page DateSplitField.
33047  * 
33048  */
33049
33050
33051 /**
33052  * @class Roo.bootstrap.DateSplitField
33053  * @extends Roo.bootstrap.Component
33054  * Bootstrap DateSplitField class
33055  * @cfg {string} fieldLabel - the label associated
33056  * @cfg {Number} labelWidth set the width of label (0-12)
33057  * @cfg {String} labelAlign (top|left)
33058  * @cfg {Boolean} dayAllowBlank (true|false) default false
33059  * @cfg {Boolean} monthAllowBlank (true|false) default false
33060  * @cfg {Boolean} yearAllowBlank (true|false) default false
33061  * @cfg {string} dayPlaceholder 
33062  * @cfg {string} monthPlaceholder
33063  * @cfg {string} yearPlaceholder
33064  * @cfg {string} dayFormat default 'd'
33065  * @cfg {string} monthFormat default 'm'
33066  * @cfg {string} yearFormat default 'Y'
33067  * @cfg {Number} labellg set the width of label (1-12)
33068  * @cfg {Number} labelmd set the width of label (1-12)
33069  * @cfg {Number} labelsm set the width of label (1-12)
33070  * @cfg {Number} labelxs set the width of label (1-12)
33071
33072  *     
33073  * @constructor
33074  * Create a new DateSplitField
33075  * @param {Object} config The config object
33076  */
33077
33078 Roo.bootstrap.DateSplitField = function(config){
33079     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33080     
33081     this.addEvents({
33082         // raw events
33083          /**
33084          * @event years
33085          * getting the data of years
33086          * @param {Roo.bootstrap.DateSplitField} this
33087          * @param {Object} years
33088          */
33089         "years" : true,
33090         /**
33091          * @event days
33092          * getting the data of days
33093          * @param {Roo.bootstrap.DateSplitField} this
33094          * @param {Object} days
33095          */
33096         "days" : true,
33097         /**
33098          * @event invalid
33099          * Fires after the field has been marked as invalid.
33100          * @param {Roo.form.Field} this
33101          * @param {String} msg The validation message
33102          */
33103         invalid : true,
33104        /**
33105          * @event valid
33106          * Fires after the field has been validated with no errors.
33107          * @param {Roo.form.Field} this
33108          */
33109         valid : true
33110     });
33111 };
33112
33113 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33114     
33115     fieldLabel : '',
33116     labelAlign : 'top',
33117     labelWidth : 3,
33118     dayAllowBlank : false,
33119     monthAllowBlank : false,
33120     yearAllowBlank : false,
33121     dayPlaceholder : '',
33122     monthPlaceholder : '',
33123     yearPlaceholder : '',
33124     dayFormat : 'd',
33125     monthFormat : 'm',
33126     yearFormat : 'Y',
33127     isFormField : true,
33128     labellg : 0,
33129     labelmd : 0,
33130     labelsm : 0,
33131     labelxs : 0,
33132     
33133     getAutoCreate : function()
33134     {
33135         var cfg = {
33136             tag : 'div',
33137             cls : 'row roo-date-split-field-group',
33138             cn : [
33139                 {
33140                     tag : 'input',
33141                     type : 'hidden',
33142                     cls : 'form-hidden-field roo-date-split-field-group-value',
33143                     name : this.name
33144                 }
33145             ]
33146         };
33147         
33148         var labelCls = 'col-md-12';
33149         var contentCls = 'col-md-4';
33150         
33151         if(this.fieldLabel){
33152             
33153             var label = {
33154                 tag : 'div',
33155                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33156                 cn : [
33157                     {
33158                         tag : 'label',
33159                         html : this.fieldLabel
33160                     }
33161                 ]
33162             };
33163             
33164             if(this.labelAlign == 'left'){
33165             
33166                 if(this.labelWidth > 12){
33167                     label.style = "width: " + this.labelWidth + 'px';
33168                 }
33169
33170                 if(this.labelWidth < 13 && this.labelmd == 0){
33171                     this.labelmd = this.labelWidth;
33172                 }
33173
33174                 if(this.labellg > 0){
33175                     labelCls = ' col-lg-' + this.labellg;
33176                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33177                 }
33178
33179                 if(this.labelmd > 0){
33180                     labelCls = ' col-md-' + this.labelmd;
33181                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33182                 }
33183
33184                 if(this.labelsm > 0){
33185                     labelCls = ' col-sm-' + this.labelsm;
33186                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33187                 }
33188
33189                 if(this.labelxs > 0){
33190                     labelCls = ' col-xs-' + this.labelxs;
33191                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33192                 }
33193             }
33194             
33195             label.cls += ' ' + labelCls;
33196             
33197             cfg.cn.push(label);
33198         }
33199         
33200         Roo.each(['day', 'month', 'year'], function(t){
33201             cfg.cn.push({
33202                 tag : 'div',
33203                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33204             });
33205         }, this);
33206         
33207         return cfg;
33208     },
33209     
33210     inputEl: function ()
33211     {
33212         return this.el.select('.roo-date-split-field-group-value', true).first();
33213     },
33214     
33215     onRender : function(ct, position) 
33216     {
33217         var _this = this;
33218         
33219         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33220         
33221         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33222         
33223         this.dayField = new Roo.bootstrap.ComboBox({
33224             allowBlank : this.dayAllowBlank,
33225             alwaysQuery : true,
33226             displayField : 'value',
33227             editable : false,
33228             fieldLabel : '',
33229             forceSelection : true,
33230             mode : 'local',
33231             placeholder : this.dayPlaceholder,
33232             selectOnFocus : true,
33233             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33234             triggerAction : 'all',
33235             typeAhead : true,
33236             valueField : 'value',
33237             store : new Roo.data.SimpleStore({
33238                 data : (function() {    
33239                     var days = [];
33240                     _this.fireEvent('days', _this, days);
33241                     return days;
33242                 })(),
33243                 fields : [ 'value' ]
33244             }),
33245             listeners : {
33246                 select : function (_self, record, index)
33247                 {
33248                     _this.setValue(_this.getValue());
33249                 }
33250             }
33251         });
33252
33253         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33254         
33255         this.monthField = new Roo.bootstrap.MonthField({
33256             after : '<i class=\"fa fa-calendar\"></i>',
33257             allowBlank : this.monthAllowBlank,
33258             placeholder : this.monthPlaceholder,
33259             readOnly : true,
33260             listeners : {
33261                 render : function (_self)
33262                 {
33263                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33264                         e.preventDefault();
33265                         _self.focus();
33266                     });
33267                 },
33268                 select : function (_self, oldvalue, newvalue)
33269                 {
33270                     _this.setValue(_this.getValue());
33271                 }
33272             }
33273         });
33274         
33275         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33276         
33277         this.yearField = new Roo.bootstrap.ComboBox({
33278             allowBlank : this.yearAllowBlank,
33279             alwaysQuery : true,
33280             displayField : 'value',
33281             editable : false,
33282             fieldLabel : '',
33283             forceSelection : true,
33284             mode : 'local',
33285             placeholder : this.yearPlaceholder,
33286             selectOnFocus : true,
33287             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33288             triggerAction : 'all',
33289             typeAhead : true,
33290             valueField : 'value',
33291             store : new Roo.data.SimpleStore({
33292                 data : (function() {
33293                     var years = [];
33294                     _this.fireEvent('years', _this, years);
33295                     return years;
33296                 })(),
33297                 fields : [ 'value' ]
33298             }),
33299             listeners : {
33300                 select : function (_self, record, index)
33301                 {
33302                     _this.setValue(_this.getValue());
33303                 }
33304             }
33305         });
33306
33307         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33308     },
33309     
33310     setValue : function(v, format)
33311     {
33312         this.inputEl.dom.value = v;
33313         
33314         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33315         
33316         var d = Date.parseDate(v, f);
33317         
33318         if(!d){
33319             this.validate();
33320             return;
33321         }
33322         
33323         this.setDay(d.format(this.dayFormat));
33324         this.setMonth(d.format(this.monthFormat));
33325         this.setYear(d.format(this.yearFormat));
33326         
33327         this.validate();
33328         
33329         return;
33330     },
33331     
33332     setDay : function(v)
33333     {
33334         this.dayField.setValue(v);
33335         this.inputEl.dom.value = this.getValue();
33336         this.validate();
33337         return;
33338     },
33339     
33340     setMonth : function(v)
33341     {
33342         this.monthField.setValue(v, true);
33343         this.inputEl.dom.value = this.getValue();
33344         this.validate();
33345         return;
33346     },
33347     
33348     setYear : function(v)
33349     {
33350         this.yearField.setValue(v);
33351         this.inputEl.dom.value = this.getValue();
33352         this.validate();
33353         return;
33354     },
33355     
33356     getDay : function()
33357     {
33358         return this.dayField.getValue();
33359     },
33360     
33361     getMonth : function()
33362     {
33363         return this.monthField.getValue();
33364     },
33365     
33366     getYear : function()
33367     {
33368         return this.yearField.getValue();
33369     },
33370     
33371     getValue : function()
33372     {
33373         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33374         
33375         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33376         
33377         return date;
33378     },
33379     
33380     reset : function()
33381     {
33382         this.setDay('');
33383         this.setMonth('');
33384         this.setYear('');
33385         this.inputEl.dom.value = '';
33386         this.validate();
33387         return;
33388     },
33389     
33390     validate : function()
33391     {
33392         var d = this.dayField.validate();
33393         var m = this.monthField.validate();
33394         var y = this.yearField.validate();
33395         
33396         var valid = true;
33397         
33398         if(
33399                 (!this.dayAllowBlank && !d) ||
33400                 (!this.monthAllowBlank && !m) ||
33401                 (!this.yearAllowBlank && !y)
33402         ){
33403             valid = false;
33404         }
33405         
33406         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33407             return valid;
33408         }
33409         
33410         if(valid){
33411             this.markValid();
33412             return valid;
33413         }
33414         
33415         this.markInvalid();
33416         
33417         return valid;
33418     },
33419     
33420     markValid : function()
33421     {
33422         
33423         var label = this.el.select('label', true).first();
33424         var icon = this.el.select('i.fa-star', true).first();
33425
33426         if(label && icon){
33427             icon.remove();
33428         }
33429         
33430         this.fireEvent('valid', this);
33431     },
33432     
33433      /**
33434      * Mark this field as invalid
33435      * @param {String} msg The validation message
33436      */
33437     markInvalid : function(msg)
33438     {
33439         
33440         var label = this.el.select('label', true).first();
33441         var icon = this.el.select('i.fa-star', true).first();
33442
33443         if(label && !icon){
33444             this.el.select('.roo-date-split-field-label', true).createChild({
33445                 tag : 'i',
33446                 cls : 'text-danger fa fa-lg fa-star',
33447                 tooltip : 'This field is required',
33448                 style : 'margin-right:5px;'
33449             }, label, true);
33450         }
33451         
33452         this.fireEvent('invalid', this, msg);
33453     },
33454     
33455     clearInvalid : function()
33456     {
33457         var label = this.el.select('label', true).first();
33458         var icon = this.el.select('i.fa-star', true).first();
33459
33460         if(label && icon){
33461             icon.remove();
33462         }
33463         
33464         this.fireEvent('valid', this);
33465     },
33466     
33467     getName: function()
33468     {
33469         return this.name;
33470     }
33471     
33472 });
33473
33474  /**
33475  *
33476  * This is based on 
33477  * http://masonry.desandro.com
33478  *
33479  * The idea is to render all the bricks based on vertical width...
33480  *
33481  * The original code extends 'outlayer' - we might need to use that....
33482  * 
33483  */
33484
33485
33486 /**
33487  * @class Roo.bootstrap.LayoutMasonry
33488  * @extends Roo.bootstrap.Component
33489  * Bootstrap Layout Masonry class
33490  * 
33491  * @constructor
33492  * Create a new Element
33493  * @param {Object} config The config object
33494  */
33495
33496 Roo.bootstrap.LayoutMasonry = function(config){
33497     
33498     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33499     
33500     this.bricks = [];
33501     
33502     Roo.bootstrap.LayoutMasonry.register(this);
33503     
33504     this.addEvents({
33505         // raw events
33506         /**
33507          * @event layout
33508          * Fire after layout the items
33509          * @param {Roo.bootstrap.LayoutMasonry} this
33510          * @param {Roo.EventObject} e
33511          */
33512         "layout" : true
33513     });
33514     
33515 };
33516
33517 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33518     
33519     /**
33520      * @cfg {Boolean} isLayoutInstant = no animation?
33521      */   
33522     isLayoutInstant : false, // needed?
33523    
33524     /**
33525      * @cfg {Number} boxWidth  width of the columns
33526      */   
33527     boxWidth : 450,
33528     
33529       /**
33530      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33531      */   
33532     boxHeight : 0,
33533     
33534     /**
33535      * @cfg {Number} padWidth padding below box..
33536      */   
33537     padWidth : 10, 
33538     
33539     /**
33540      * @cfg {Number} gutter gutter width..
33541      */   
33542     gutter : 10,
33543     
33544      /**
33545      * @cfg {Number} maxCols maximum number of columns
33546      */   
33547     
33548     maxCols: 0,
33549     
33550     /**
33551      * @cfg {Boolean} isAutoInitial defalut true
33552      */   
33553     isAutoInitial : true, 
33554     
33555     containerWidth: 0,
33556     
33557     /**
33558      * @cfg {Boolean} isHorizontal defalut false
33559      */   
33560     isHorizontal : false, 
33561
33562     currentSize : null,
33563     
33564     tag: 'div',
33565     
33566     cls: '',
33567     
33568     bricks: null, //CompositeElement
33569     
33570     cols : 1,
33571     
33572     _isLayoutInited : false,
33573     
33574 //    isAlternative : false, // only use for vertical layout...
33575     
33576     /**
33577      * @cfg {Number} alternativePadWidth padding below box..
33578      */   
33579     alternativePadWidth : 50,
33580     
33581     selectedBrick : [],
33582     
33583     getAutoCreate : function(){
33584         
33585         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33586         
33587         var cfg = {
33588             tag: this.tag,
33589             cls: 'blog-masonary-wrapper ' + this.cls,
33590             cn : {
33591                 cls : 'mas-boxes masonary'
33592             }
33593         };
33594         
33595         return cfg;
33596     },
33597     
33598     getChildContainer: function( )
33599     {
33600         if (this.boxesEl) {
33601             return this.boxesEl;
33602         }
33603         
33604         this.boxesEl = this.el.select('.mas-boxes').first();
33605         
33606         return this.boxesEl;
33607     },
33608     
33609     
33610     initEvents : function()
33611     {
33612         var _this = this;
33613         
33614         if(this.isAutoInitial){
33615             Roo.log('hook children rendered');
33616             this.on('childrenrendered', function() {
33617                 Roo.log('children rendered');
33618                 _this.initial();
33619             } ,this);
33620         }
33621     },
33622     
33623     initial : function()
33624     {
33625         this.selectedBrick = [];
33626         
33627         this.currentSize = this.el.getBox(true);
33628         
33629         Roo.EventManager.onWindowResize(this.resize, this); 
33630
33631         if(!this.isAutoInitial){
33632             this.layout();
33633             return;
33634         }
33635         
33636         this.layout();
33637         
33638         return;
33639         //this.layout.defer(500,this);
33640         
33641     },
33642     
33643     resize : function()
33644     {
33645         var cs = this.el.getBox(true);
33646         
33647         if (
33648                 this.currentSize.width == cs.width && 
33649                 this.currentSize.x == cs.x && 
33650                 this.currentSize.height == cs.height && 
33651                 this.currentSize.y == cs.y 
33652         ) {
33653             Roo.log("no change in with or X or Y");
33654             return;
33655         }
33656         
33657         this.currentSize = cs;
33658         
33659         this.layout();
33660         
33661     },
33662     
33663     layout : function()
33664     {   
33665         this._resetLayout();
33666         
33667         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33668         
33669         this.layoutItems( isInstant );
33670       
33671         this._isLayoutInited = true;
33672         
33673         this.fireEvent('layout', this);
33674         
33675     },
33676     
33677     _resetLayout : function()
33678     {
33679         if(this.isHorizontal){
33680             this.horizontalMeasureColumns();
33681             return;
33682         }
33683         
33684         this.verticalMeasureColumns();
33685         
33686     },
33687     
33688     verticalMeasureColumns : function()
33689     {
33690         this.getContainerWidth();
33691         
33692 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33693 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33694 //            return;
33695 //        }
33696         
33697         var boxWidth = this.boxWidth + this.padWidth;
33698         
33699         if(this.containerWidth < this.boxWidth){
33700             boxWidth = this.containerWidth
33701         }
33702         
33703         var containerWidth = this.containerWidth;
33704         
33705         var cols = Math.floor(containerWidth / boxWidth);
33706         
33707         this.cols = Math.max( cols, 1 );
33708         
33709         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33710         
33711         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33712         
33713         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33714         
33715         this.colWidth = boxWidth + avail - this.padWidth;
33716         
33717         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33718         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33719     },
33720     
33721     horizontalMeasureColumns : function()
33722     {
33723         this.getContainerWidth();
33724         
33725         var boxWidth = this.boxWidth;
33726         
33727         if(this.containerWidth < boxWidth){
33728             boxWidth = this.containerWidth;
33729         }
33730         
33731         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33732         
33733         this.el.setHeight(boxWidth);
33734         
33735     },
33736     
33737     getContainerWidth : function()
33738     {
33739         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33740     },
33741     
33742     layoutItems : function( isInstant )
33743     {
33744         Roo.log(this.bricks);
33745         
33746         var items = Roo.apply([], this.bricks);
33747         
33748         if(this.isHorizontal){
33749             this._horizontalLayoutItems( items , isInstant );
33750             return;
33751         }
33752         
33753 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33754 //            this._verticalAlternativeLayoutItems( items , isInstant );
33755 //            return;
33756 //        }
33757         
33758         this._verticalLayoutItems( items , isInstant );
33759         
33760     },
33761     
33762     _verticalLayoutItems : function ( items , isInstant)
33763     {
33764         if ( !items || !items.length ) {
33765             return;
33766         }
33767         
33768         var standard = [
33769             ['xs', 'xs', 'xs', 'tall'],
33770             ['xs', 'xs', 'tall'],
33771             ['xs', 'xs', 'sm'],
33772             ['xs', 'xs', 'xs'],
33773             ['xs', 'tall'],
33774             ['xs', 'sm'],
33775             ['xs', 'xs'],
33776             ['xs'],
33777             
33778             ['sm', 'xs', 'xs'],
33779             ['sm', 'xs'],
33780             ['sm'],
33781             
33782             ['tall', 'xs', 'xs', 'xs'],
33783             ['tall', 'xs', 'xs'],
33784             ['tall', 'xs'],
33785             ['tall']
33786             
33787         ];
33788         
33789         var queue = [];
33790         
33791         var boxes = [];
33792         
33793         var box = [];
33794         
33795         Roo.each(items, function(item, k){
33796             
33797             switch (item.size) {
33798                 // these layouts take up a full box,
33799                 case 'md' :
33800                 case 'md-left' :
33801                 case 'md-right' :
33802                 case 'wide' :
33803                     
33804                     if(box.length){
33805                         boxes.push(box);
33806                         box = [];
33807                     }
33808                     
33809                     boxes.push([item]);
33810                     
33811                     break;
33812                     
33813                 case 'xs' :
33814                 case 'sm' :
33815                 case 'tall' :
33816                     
33817                     box.push(item);
33818                     
33819                     break;
33820                 default :
33821                     break;
33822                     
33823             }
33824             
33825         }, this);
33826         
33827         if(box.length){
33828             boxes.push(box);
33829             box = [];
33830         }
33831         
33832         var filterPattern = function(box, length)
33833         {
33834             if(!box.length){
33835                 return;
33836             }
33837             
33838             var match = false;
33839             
33840             var pattern = box.slice(0, length);
33841             
33842             var format = [];
33843             
33844             Roo.each(pattern, function(i){
33845                 format.push(i.size);
33846             }, this);
33847             
33848             Roo.each(standard, function(s){
33849                 
33850                 if(String(s) != String(format)){
33851                     return;
33852                 }
33853                 
33854                 match = true;
33855                 return false;
33856                 
33857             }, this);
33858             
33859             if(!match && length == 1){
33860                 return;
33861             }
33862             
33863             if(!match){
33864                 filterPattern(box, length - 1);
33865                 return;
33866             }
33867                 
33868             queue.push(pattern);
33869
33870             box = box.slice(length, box.length);
33871
33872             filterPattern(box, 4);
33873
33874             return;
33875             
33876         }
33877         
33878         Roo.each(boxes, function(box, k){
33879             
33880             if(!box.length){
33881                 return;
33882             }
33883             
33884             if(box.length == 1){
33885                 queue.push(box);
33886                 return;
33887             }
33888             
33889             filterPattern(box, 4);
33890             
33891         }, this);
33892         
33893         this._processVerticalLayoutQueue( queue, isInstant );
33894         
33895     },
33896     
33897 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33898 //    {
33899 //        if ( !items || !items.length ) {
33900 //            return;
33901 //        }
33902 //
33903 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33904 //        
33905 //    },
33906     
33907     _horizontalLayoutItems : function ( items , isInstant)
33908     {
33909         if ( !items || !items.length || items.length < 3) {
33910             return;
33911         }
33912         
33913         items.reverse();
33914         
33915         var eItems = items.slice(0, 3);
33916         
33917         items = items.slice(3, items.length);
33918         
33919         var standard = [
33920             ['xs', 'xs', 'xs', 'wide'],
33921             ['xs', 'xs', 'wide'],
33922             ['xs', 'xs', 'sm'],
33923             ['xs', 'xs', 'xs'],
33924             ['xs', 'wide'],
33925             ['xs', 'sm'],
33926             ['xs', 'xs'],
33927             ['xs'],
33928             
33929             ['sm', 'xs', 'xs'],
33930             ['sm', 'xs'],
33931             ['sm'],
33932             
33933             ['wide', 'xs', 'xs', 'xs'],
33934             ['wide', 'xs', 'xs'],
33935             ['wide', 'xs'],
33936             ['wide'],
33937             
33938             ['wide-thin']
33939         ];
33940         
33941         var queue = [];
33942         
33943         var boxes = [];
33944         
33945         var box = [];
33946         
33947         Roo.each(items, function(item, k){
33948             
33949             switch (item.size) {
33950                 case 'md' :
33951                 case 'md-left' :
33952                 case 'md-right' :
33953                 case 'tall' :
33954                     
33955                     if(box.length){
33956                         boxes.push(box);
33957                         box = [];
33958                     }
33959                     
33960                     boxes.push([item]);
33961                     
33962                     break;
33963                     
33964                 case 'xs' :
33965                 case 'sm' :
33966                 case 'wide' :
33967                 case 'wide-thin' :
33968                     
33969                     box.push(item);
33970                     
33971                     break;
33972                 default :
33973                     break;
33974                     
33975             }
33976             
33977         }, this);
33978         
33979         if(box.length){
33980             boxes.push(box);
33981             box = [];
33982         }
33983         
33984         var filterPattern = function(box, length)
33985         {
33986             if(!box.length){
33987                 return;
33988             }
33989             
33990             var match = false;
33991             
33992             var pattern = box.slice(0, length);
33993             
33994             var format = [];
33995             
33996             Roo.each(pattern, function(i){
33997                 format.push(i.size);
33998             }, this);
33999             
34000             Roo.each(standard, function(s){
34001                 
34002                 if(String(s) != String(format)){
34003                     return;
34004                 }
34005                 
34006                 match = true;
34007                 return false;
34008                 
34009             }, this);
34010             
34011             if(!match && length == 1){
34012                 return;
34013             }
34014             
34015             if(!match){
34016                 filterPattern(box, length - 1);
34017                 return;
34018             }
34019                 
34020             queue.push(pattern);
34021
34022             box = box.slice(length, box.length);
34023
34024             filterPattern(box, 4);
34025
34026             return;
34027             
34028         }
34029         
34030         Roo.each(boxes, function(box, k){
34031             
34032             if(!box.length){
34033                 return;
34034             }
34035             
34036             if(box.length == 1){
34037                 queue.push(box);
34038                 return;
34039             }
34040             
34041             filterPattern(box, 4);
34042             
34043         }, this);
34044         
34045         
34046         var prune = [];
34047         
34048         var pos = this.el.getBox(true);
34049         
34050         var minX = pos.x;
34051         
34052         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34053         
34054         var hit_end = false;
34055         
34056         Roo.each(queue, function(box){
34057             
34058             if(hit_end){
34059                 
34060                 Roo.each(box, function(b){
34061                 
34062                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34063                     b.el.hide();
34064
34065                 }, this);
34066
34067                 return;
34068             }
34069             
34070             var mx = 0;
34071             
34072             Roo.each(box, function(b){
34073                 
34074                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34075                 b.el.show();
34076
34077                 mx = Math.max(mx, b.x);
34078                 
34079             }, this);
34080             
34081             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34082             
34083             if(maxX < minX){
34084                 
34085                 Roo.each(box, function(b){
34086                 
34087                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34088                     b.el.hide();
34089                     
34090                 }, this);
34091                 
34092                 hit_end = true;
34093                 
34094                 return;
34095             }
34096             
34097             prune.push(box);
34098             
34099         }, this);
34100         
34101         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34102     },
34103     
34104     /** Sets position of item in DOM
34105     * @param {Element} item
34106     * @param {Number} x - horizontal position
34107     * @param {Number} y - vertical position
34108     * @param {Boolean} isInstant - disables transitions
34109     */
34110     _processVerticalLayoutQueue : function( queue, isInstant )
34111     {
34112         var pos = this.el.getBox(true);
34113         var x = pos.x;
34114         var y = pos.y;
34115         var maxY = [];
34116         
34117         for (var i = 0; i < this.cols; i++){
34118             maxY[i] = pos.y;
34119         }
34120         
34121         Roo.each(queue, function(box, k){
34122             
34123             var col = k % this.cols;
34124             
34125             Roo.each(box, function(b,kk){
34126                 
34127                 b.el.position('absolute');
34128                 
34129                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34130                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34131                 
34132                 if(b.size == 'md-left' || b.size == 'md-right'){
34133                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34134                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34135                 }
34136                 
34137                 b.el.setWidth(width);
34138                 b.el.setHeight(height);
34139                 // iframe?
34140                 b.el.select('iframe',true).setSize(width,height);
34141                 
34142             }, this);
34143             
34144             for (var i = 0; i < this.cols; i++){
34145                 
34146                 if(maxY[i] < maxY[col]){
34147                     col = i;
34148                     continue;
34149                 }
34150                 
34151                 col = Math.min(col, i);
34152                 
34153             }
34154             
34155             x = pos.x + col * (this.colWidth + this.padWidth);
34156             
34157             y = maxY[col];
34158             
34159             var positions = [];
34160             
34161             switch (box.length){
34162                 case 1 :
34163                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34164                     break;
34165                 case 2 :
34166                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34167                     break;
34168                 case 3 :
34169                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34170                     break;
34171                 case 4 :
34172                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34173                     break;
34174                 default :
34175                     break;
34176             }
34177             
34178             Roo.each(box, function(b,kk){
34179                 
34180                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34181                 
34182                 var sz = b.el.getSize();
34183                 
34184                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34185                 
34186             }, this);
34187             
34188         }, this);
34189         
34190         var mY = 0;
34191         
34192         for (var i = 0; i < this.cols; i++){
34193             mY = Math.max(mY, maxY[i]);
34194         }
34195         
34196         this.el.setHeight(mY - pos.y);
34197         
34198     },
34199     
34200 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34201 //    {
34202 //        var pos = this.el.getBox(true);
34203 //        var x = pos.x;
34204 //        var y = pos.y;
34205 //        var maxX = pos.right;
34206 //        
34207 //        var maxHeight = 0;
34208 //        
34209 //        Roo.each(items, function(item, k){
34210 //            
34211 //            var c = k % 2;
34212 //            
34213 //            item.el.position('absolute');
34214 //                
34215 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34216 //
34217 //            item.el.setWidth(width);
34218 //
34219 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34220 //
34221 //            item.el.setHeight(height);
34222 //            
34223 //            if(c == 0){
34224 //                item.el.setXY([x, y], isInstant ? false : true);
34225 //            } else {
34226 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34227 //            }
34228 //            
34229 //            y = y + height + this.alternativePadWidth;
34230 //            
34231 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34232 //            
34233 //        }, this);
34234 //        
34235 //        this.el.setHeight(maxHeight);
34236 //        
34237 //    },
34238     
34239     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34240     {
34241         var pos = this.el.getBox(true);
34242         
34243         var minX = pos.x;
34244         var minY = pos.y;
34245         
34246         var maxX = pos.right;
34247         
34248         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34249         
34250         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34251         
34252         Roo.each(queue, function(box, k){
34253             
34254             Roo.each(box, function(b, kk){
34255                 
34256                 b.el.position('absolute');
34257                 
34258                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34259                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34260                 
34261                 if(b.size == 'md-left' || b.size == 'md-right'){
34262                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34263                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34264                 }
34265                 
34266                 b.el.setWidth(width);
34267                 b.el.setHeight(height);
34268                 
34269             }, this);
34270             
34271             if(!box.length){
34272                 return;
34273             }
34274             
34275             var positions = [];
34276             
34277             switch (box.length){
34278                 case 1 :
34279                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34280                     break;
34281                 case 2 :
34282                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34283                     break;
34284                 case 3 :
34285                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34286                     break;
34287                 case 4 :
34288                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34289                     break;
34290                 default :
34291                     break;
34292             }
34293             
34294             Roo.each(box, function(b,kk){
34295                 
34296                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34297                 
34298                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34299                 
34300             }, this);
34301             
34302         }, this);
34303         
34304     },
34305     
34306     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34307     {
34308         Roo.each(eItems, function(b,k){
34309             
34310             b.size = (k == 0) ? 'sm' : 'xs';
34311             b.x = (k == 0) ? 2 : 1;
34312             b.y = (k == 0) ? 2 : 1;
34313             
34314             b.el.position('absolute');
34315             
34316             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34317                 
34318             b.el.setWidth(width);
34319             
34320             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34321             
34322             b.el.setHeight(height);
34323             
34324         }, this);
34325
34326         var positions = [];
34327         
34328         positions.push({
34329             x : maxX - this.unitWidth * 2 - this.gutter,
34330             y : minY
34331         });
34332         
34333         positions.push({
34334             x : maxX - this.unitWidth,
34335             y : minY + (this.unitWidth + this.gutter) * 2
34336         });
34337         
34338         positions.push({
34339             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34340             y : minY
34341         });
34342         
34343         Roo.each(eItems, function(b,k){
34344             
34345             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34346
34347         }, this);
34348         
34349     },
34350     
34351     getVerticalOneBoxColPositions : function(x, y, box)
34352     {
34353         var pos = [];
34354         
34355         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34356         
34357         if(box[0].size == 'md-left'){
34358             rand = 0;
34359         }
34360         
34361         if(box[0].size == 'md-right'){
34362             rand = 1;
34363         }
34364         
34365         pos.push({
34366             x : x + (this.unitWidth + this.gutter) * rand,
34367             y : y
34368         });
34369         
34370         return pos;
34371     },
34372     
34373     getVerticalTwoBoxColPositions : function(x, y, box)
34374     {
34375         var pos = [];
34376         
34377         if(box[0].size == 'xs'){
34378             
34379             pos.push({
34380                 x : x,
34381                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34382             });
34383
34384             pos.push({
34385                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34386                 y : y
34387             });
34388             
34389             return pos;
34390             
34391         }
34392         
34393         pos.push({
34394             x : x,
34395             y : y
34396         });
34397
34398         pos.push({
34399             x : x + (this.unitWidth + this.gutter) * 2,
34400             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34401         });
34402         
34403         return pos;
34404         
34405     },
34406     
34407     getVerticalThreeBoxColPositions : function(x, y, box)
34408     {
34409         var pos = [];
34410         
34411         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34412             
34413             pos.push({
34414                 x : x,
34415                 y : y
34416             });
34417
34418             pos.push({
34419                 x : x + (this.unitWidth + this.gutter) * 1,
34420                 y : y
34421             });
34422             
34423             pos.push({
34424                 x : x + (this.unitWidth + this.gutter) * 2,
34425                 y : y
34426             });
34427             
34428             return pos;
34429             
34430         }
34431         
34432         if(box[0].size == 'xs' && box[1].size == 'xs'){
34433             
34434             pos.push({
34435                 x : x,
34436                 y : y
34437             });
34438
34439             pos.push({
34440                 x : x,
34441                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34442             });
34443             
34444             pos.push({
34445                 x : x + (this.unitWidth + this.gutter) * 1,
34446                 y : y
34447             });
34448             
34449             return pos;
34450             
34451         }
34452         
34453         pos.push({
34454             x : x,
34455             y : y
34456         });
34457
34458         pos.push({
34459             x : x + (this.unitWidth + this.gutter) * 2,
34460             y : y
34461         });
34462
34463         pos.push({
34464             x : x + (this.unitWidth + this.gutter) * 2,
34465             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34466         });
34467             
34468         return pos;
34469         
34470     },
34471     
34472     getVerticalFourBoxColPositions : function(x, y, box)
34473     {
34474         var pos = [];
34475         
34476         if(box[0].size == 'xs'){
34477             
34478             pos.push({
34479                 x : x,
34480                 y : y
34481             });
34482
34483             pos.push({
34484                 x : x,
34485                 y : y + (this.unitHeight + this.gutter) * 1
34486             });
34487             
34488             pos.push({
34489                 x : x,
34490                 y : y + (this.unitHeight + this.gutter) * 2
34491             });
34492             
34493             pos.push({
34494                 x : x + (this.unitWidth + this.gutter) * 1,
34495                 y : y
34496             });
34497             
34498             return pos;
34499             
34500         }
34501         
34502         pos.push({
34503             x : x,
34504             y : y
34505         });
34506
34507         pos.push({
34508             x : x + (this.unitWidth + this.gutter) * 2,
34509             y : y
34510         });
34511
34512         pos.push({
34513             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34514             y : y + (this.unitHeight + this.gutter) * 1
34515         });
34516
34517         pos.push({
34518             x : x + (this.unitWidth + this.gutter) * 2,
34519             y : y + (this.unitWidth + this.gutter) * 2
34520         });
34521
34522         return pos;
34523         
34524     },
34525     
34526     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34527     {
34528         var pos = [];
34529         
34530         if(box[0].size == 'md-left'){
34531             pos.push({
34532                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34533                 y : minY
34534             });
34535             
34536             return pos;
34537         }
34538         
34539         if(box[0].size == 'md-right'){
34540             pos.push({
34541                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34542                 y : minY + (this.unitWidth + this.gutter) * 1
34543             });
34544             
34545             return pos;
34546         }
34547         
34548         var rand = Math.floor(Math.random() * (4 - box[0].y));
34549         
34550         pos.push({
34551             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34552             y : minY + (this.unitWidth + this.gutter) * rand
34553         });
34554         
34555         return pos;
34556         
34557     },
34558     
34559     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34560     {
34561         var pos = [];
34562         
34563         if(box[0].size == 'xs'){
34564             
34565             pos.push({
34566                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34567                 y : minY
34568             });
34569
34570             pos.push({
34571                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34572                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34573             });
34574             
34575             return pos;
34576             
34577         }
34578         
34579         pos.push({
34580             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34581             y : minY
34582         });
34583
34584         pos.push({
34585             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34586             y : minY + (this.unitWidth + this.gutter) * 2
34587         });
34588         
34589         return pos;
34590         
34591     },
34592     
34593     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34594     {
34595         var pos = [];
34596         
34597         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34598             
34599             pos.push({
34600                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34601                 y : minY
34602             });
34603
34604             pos.push({
34605                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34606                 y : minY + (this.unitWidth + this.gutter) * 1
34607             });
34608             
34609             pos.push({
34610                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34611                 y : minY + (this.unitWidth + this.gutter) * 2
34612             });
34613             
34614             return pos;
34615             
34616         }
34617         
34618         if(box[0].size == 'xs' && box[1].size == 'xs'){
34619             
34620             pos.push({
34621                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34622                 y : minY
34623             });
34624
34625             pos.push({
34626                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34627                 y : minY
34628             });
34629             
34630             pos.push({
34631                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34632                 y : minY + (this.unitWidth + this.gutter) * 1
34633             });
34634             
34635             return pos;
34636             
34637         }
34638         
34639         pos.push({
34640             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34641             y : minY
34642         });
34643
34644         pos.push({
34645             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34646             y : minY + (this.unitWidth + this.gutter) * 2
34647         });
34648
34649         pos.push({
34650             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34651             y : minY + (this.unitWidth + this.gutter) * 2
34652         });
34653             
34654         return pos;
34655         
34656     },
34657     
34658     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34659     {
34660         var pos = [];
34661         
34662         if(box[0].size == 'xs'){
34663             
34664             pos.push({
34665                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34666                 y : minY
34667             });
34668
34669             pos.push({
34670                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34671                 y : minY
34672             });
34673             
34674             pos.push({
34675                 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),
34676                 y : minY
34677             });
34678             
34679             pos.push({
34680                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34681                 y : minY + (this.unitWidth + this.gutter) * 1
34682             });
34683             
34684             return pos;
34685             
34686         }
34687         
34688         pos.push({
34689             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34690             y : minY
34691         });
34692         
34693         pos.push({
34694             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34695             y : minY + (this.unitWidth + this.gutter) * 2
34696         });
34697         
34698         pos.push({
34699             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34700             y : minY + (this.unitWidth + this.gutter) * 2
34701         });
34702         
34703         pos.push({
34704             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),
34705             y : minY + (this.unitWidth + this.gutter) * 2
34706         });
34707
34708         return pos;
34709         
34710     },
34711     
34712     /**
34713     * remove a Masonry Brick
34714     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34715     */
34716     removeBrick : function(brick_id)
34717     {
34718         if (!brick_id) {
34719             return;
34720         }
34721         
34722         for (var i = 0; i<this.bricks.length; i++) {
34723             if (this.bricks[i].id == brick_id) {
34724                 this.bricks.splice(i,1);
34725                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34726                 this.initial();
34727             }
34728         }
34729     },
34730     
34731     /**
34732     * adds a Masonry Brick
34733     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34734     */
34735     addBrick : function(cfg)
34736     {
34737         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34738         //this.register(cn);
34739         cn.parentId = this.id;
34740         cn.render(this.el);
34741         return cn;
34742     },
34743     
34744     /**
34745     * register a Masonry Brick
34746     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34747     */
34748     
34749     register : function(brick)
34750     {
34751         this.bricks.push(brick);
34752         brick.masonryId = this.id;
34753     },
34754     
34755     /**
34756     * clear all the Masonry Brick
34757     */
34758     clearAll : function()
34759     {
34760         this.bricks = [];
34761         //this.getChildContainer().dom.innerHTML = "";
34762         this.el.dom.innerHTML = '';
34763     },
34764     
34765     getSelected : function()
34766     {
34767         if (!this.selectedBrick) {
34768             return false;
34769         }
34770         
34771         return this.selectedBrick;
34772     }
34773 });
34774
34775 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34776     
34777     groups: {},
34778      /**
34779     * register a Masonry Layout
34780     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34781     */
34782     
34783     register : function(layout)
34784     {
34785         this.groups[layout.id] = layout;
34786     },
34787     /**
34788     * fetch a  Masonry Layout based on the masonry layout ID
34789     * @param {string} the masonry layout to add
34790     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34791     */
34792     
34793     get: function(layout_id) {
34794         if (typeof(this.groups[layout_id]) == 'undefined') {
34795             return false;
34796         }
34797         return this.groups[layout_id] ;
34798     }
34799     
34800     
34801     
34802 });
34803
34804  
34805
34806  /**
34807  *
34808  * This is based on 
34809  * http://masonry.desandro.com
34810  *
34811  * The idea is to render all the bricks based on vertical width...
34812  *
34813  * The original code extends 'outlayer' - we might need to use that....
34814  * 
34815  */
34816
34817
34818 /**
34819  * @class Roo.bootstrap.LayoutMasonryAuto
34820  * @extends Roo.bootstrap.Component
34821  * Bootstrap Layout Masonry class
34822  * 
34823  * @constructor
34824  * Create a new Element
34825  * @param {Object} config The config object
34826  */
34827
34828 Roo.bootstrap.LayoutMasonryAuto = function(config){
34829     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34830 };
34831
34832 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34833     
34834       /**
34835      * @cfg {Boolean} isFitWidth  - resize the width..
34836      */   
34837     isFitWidth : false,  // options..
34838     /**
34839      * @cfg {Boolean} isOriginLeft = left align?
34840      */   
34841     isOriginLeft : true,
34842     /**
34843      * @cfg {Boolean} isOriginTop = top align?
34844      */   
34845     isOriginTop : false,
34846     /**
34847      * @cfg {Boolean} isLayoutInstant = no animation?
34848      */   
34849     isLayoutInstant : false, // needed?
34850     /**
34851      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34852      */   
34853     isResizingContainer : true,
34854     /**
34855      * @cfg {Number} columnWidth  width of the columns 
34856      */   
34857     
34858     columnWidth : 0,
34859     
34860     /**
34861      * @cfg {Number} maxCols maximum number of columns
34862      */   
34863     
34864     maxCols: 0,
34865     /**
34866      * @cfg {Number} padHeight padding below box..
34867      */   
34868     
34869     padHeight : 10, 
34870     
34871     /**
34872      * @cfg {Boolean} isAutoInitial defalut true
34873      */   
34874     
34875     isAutoInitial : true, 
34876     
34877     // private?
34878     gutter : 0,
34879     
34880     containerWidth: 0,
34881     initialColumnWidth : 0,
34882     currentSize : null,
34883     
34884     colYs : null, // array.
34885     maxY : 0,
34886     padWidth: 10,
34887     
34888     
34889     tag: 'div',
34890     cls: '',
34891     bricks: null, //CompositeElement
34892     cols : 0, // array?
34893     // element : null, // wrapped now this.el
34894     _isLayoutInited : null, 
34895     
34896     
34897     getAutoCreate : function(){
34898         
34899         var cfg = {
34900             tag: this.tag,
34901             cls: 'blog-masonary-wrapper ' + this.cls,
34902             cn : {
34903                 cls : 'mas-boxes masonary'
34904             }
34905         };
34906         
34907         return cfg;
34908     },
34909     
34910     getChildContainer: function( )
34911     {
34912         if (this.boxesEl) {
34913             return this.boxesEl;
34914         }
34915         
34916         this.boxesEl = this.el.select('.mas-boxes').first();
34917         
34918         return this.boxesEl;
34919     },
34920     
34921     
34922     initEvents : function()
34923     {
34924         var _this = this;
34925         
34926         if(this.isAutoInitial){
34927             Roo.log('hook children rendered');
34928             this.on('childrenrendered', function() {
34929                 Roo.log('children rendered');
34930                 _this.initial();
34931             } ,this);
34932         }
34933         
34934     },
34935     
34936     initial : function()
34937     {
34938         this.reloadItems();
34939
34940         this.currentSize = this.el.getBox(true);
34941
34942         /// was window resize... - let's see if this works..
34943         Roo.EventManager.onWindowResize(this.resize, this); 
34944
34945         if(!this.isAutoInitial){
34946             this.layout();
34947             return;
34948         }
34949         
34950         this.layout.defer(500,this);
34951     },
34952     
34953     reloadItems: function()
34954     {
34955         this.bricks = this.el.select('.masonry-brick', true);
34956         
34957         this.bricks.each(function(b) {
34958             //Roo.log(b.getSize());
34959             if (!b.attr('originalwidth')) {
34960                 b.attr('originalwidth',  b.getSize().width);
34961             }
34962             
34963         });
34964         
34965         Roo.log(this.bricks.elements.length);
34966     },
34967     
34968     resize : function()
34969     {
34970         Roo.log('resize');
34971         var cs = this.el.getBox(true);
34972         
34973         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34974             Roo.log("no change in with or X");
34975             return;
34976         }
34977         this.currentSize = cs;
34978         this.layout();
34979     },
34980     
34981     layout : function()
34982     {
34983          Roo.log('layout');
34984         this._resetLayout();
34985         //this._manageStamps();
34986       
34987         // don't animate first layout
34988         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34989         this.layoutItems( isInstant );
34990       
34991         // flag for initalized
34992         this._isLayoutInited = true;
34993     },
34994     
34995     layoutItems : function( isInstant )
34996     {
34997         //var items = this._getItemsForLayout( this.items );
34998         // original code supports filtering layout items.. we just ignore it..
34999         
35000         this._layoutItems( this.bricks , isInstant );
35001       
35002         this._postLayout();
35003     },
35004     _layoutItems : function ( items , isInstant)
35005     {
35006        //this.fireEvent( 'layout', this, items );
35007     
35008
35009         if ( !items || !items.elements.length ) {
35010           // no items, emit event with empty array
35011             return;
35012         }
35013
35014         var queue = [];
35015         items.each(function(item) {
35016             Roo.log("layout item");
35017             Roo.log(item);
35018             // get x/y object from method
35019             var position = this._getItemLayoutPosition( item );
35020             // enqueue
35021             position.item = item;
35022             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35023             queue.push( position );
35024         }, this);
35025       
35026         this._processLayoutQueue( queue );
35027     },
35028     /** Sets position of item in DOM
35029     * @param {Element} item
35030     * @param {Number} x - horizontal position
35031     * @param {Number} y - vertical position
35032     * @param {Boolean} isInstant - disables transitions
35033     */
35034     _processLayoutQueue : function( queue )
35035     {
35036         for ( var i=0, len = queue.length; i < len; i++ ) {
35037             var obj = queue[i];
35038             obj.item.position('absolute');
35039             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35040         }
35041     },
35042       
35043     
35044     /**
35045     * Any logic you want to do after each layout,
35046     * i.e. size the container
35047     */
35048     _postLayout : function()
35049     {
35050         this.resizeContainer();
35051     },
35052     
35053     resizeContainer : function()
35054     {
35055         if ( !this.isResizingContainer ) {
35056             return;
35057         }
35058         var size = this._getContainerSize();
35059         if ( size ) {
35060             this.el.setSize(size.width,size.height);
35061             this.boxesEl.setSize(size.width,size.height);
35062         }
35063     },
35064     
35065     
35066     
35067     _resetLayout : function()
35068     {
35069         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35070         this.colWidth = this.el.getWidth();
35071         //this.gutter = this.el.getWidth(); 
35072         
35073         this.measureColumns();
35074
35075         // reset column Y
35076         var i = this.cols;
35077         this.colYs = [];
35078         while (i--) {
35079             this.colYs.push( 0 );
35080         }
35081     
35082         this.maxY = 0;
35083     },
35084
35085     measureColumns : function()
35086     {
35087         this.getContainerWidth();
35088       // if columnWidth is 0, default to outerWidth of first item
35089         if ( !this.columnWidth ) {
35090             var firstItem = this.bricks.first();
35091             Roo.log(firstItem);
35092             this.columnWidth  = this.containerWidth;
35093             if (firstItem && firstItem.attr('originalwidth') ) {
35094                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35095             }
35096             // columnWidth fall back to item of first element
35097             Roo.log("set column width?");
35098                         this.initialColumnWidth = this.columnWidth  ;
35099
35100             // if first elem has no width, default to size of container
35101             
35102         }
35103         
35104         
35105         if (this.initialColumnWidth) {
35106             this.columnWidth = this.initialColumnWidth;
35107         }
35108         
35109         
35110             
35111         // column width is fixed at the top - however if container width get's smaller we should
35112         // reduce it...
35113         
35114         // this bit calcs how man columns..
35115             
35116         var columnWidth = this.columnWidth += this.gutter;
35117       
35118         // calculate columns
35119         var containerWidth = this.containerWidth + this.gutter;
35120         
35121         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35122         // fix rounding errors, typically with gutters
35123         var excess = columnWidth - containerWidth % columnWidth;
35124         
35125         
35126         // if overshoot is less than a pixel, round up, otherwise floor it
35127         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35128         cols = Math[ mathMethod ]( cols );
35129         this.cols = Math.max( cols, 1 );
35130         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35131         
35132          // padding positioning..
35133         var totalColWidth = this.cols * this.columnWidth;
35134         var padavail = this.containerWidth - totalColWidth;
35135         // so for 2 columns - we need 3 'pads'
35136         
35137         var padNeeded = (1+this.cols) * this.padWidth;
35138         
35139         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35140         
35141         this.columnWidth += padExtra
35142         //this.padWidth = Math.floor(padavail /  ( this.cols));
35143         
35144         // adjust colum width so that padding is fixed??
35145         
35146         // we have 3 columns ... total = width * 3
35147         // we have X left over... that should be used by 
35148         
35149         //if (this.expandC) {
35150             
35151         //}
35152         
35153         
35154         
35155     },
35156     
35157     getContainerWidth : function()
35158     {
35159        /* // container is parent if fit width
35160         var container = this.isFitWidth ? this.element.parentNode : this.element;
35161         // check that this.size and size are there
35162         // IE8 triggers resize on body size change, so they might not be
35163         
35164         var size = getSize( container );  //FIXME
35165         this.containerWidth = size && size.innerWidth; //FIXME
35166         */
35167          
35168         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35169         
35170     },
35171     
35172     _getItemLayoutPosition : function( item )  // what is item?
35173     {
35174         // we resize the item to our columnWidth..
35175       
35176         item.setWidth(this.columnWidth);
35177         item.autoBoxAdjust  = false;
35178         
35179         var sz = item.getSize();
35180  
35181         // how many columns does this brick span
35182         var remainder = this.containerWidth % this.columnWidth;
35183         
35184         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35185         // round if off by 1 pixel, otherwise use ceil
35186         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35187         colSpan = Math.min( colSpan, this.cols );
35188         
35189         // normally this should be '1' as we dont' currently allow multi width columns..
35190         
35191         var colGroup = this._getColGroup( colSpan );
35192         // get the minimum Y value from the columns
35193         var minimumY = Math.min.apply( Math, colGroup );
35194         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35195         
35196         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35197          
35198         // position the brick
35199         var position = {
35200             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35201             y: this.currentSize.y + minimumY + this.padHeight
35202         };
35203         
35204         Roo.log(position);
35205         // apply setHeight to necessary columns
35206         var setHeight = minimumY + sz.height + this.padHeight;
35207         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35208         
35209         var setSpan = this.cols + 1 - colGroup.length;
35210         for ( var i = 0; i < setSpan; i++ ) {
35211           this.colYs[ shortColIndex + i ] = setHeight ;
35212         }
35213       
35214         return position;
35215     },
35216     
35217     /**
35218      * @param {Number} colSpan - number of columns the element spans
35219      * @returns {Array} colGroup
35220      */
35221     _getColGroup : function( colSpan )
35222     {
35223         if ( colSpan < 2 ) {
35224           // if brick spans only one column, use all the column Ys
35225           return this.colYs;
35226         }
35227       
35228         var colGroup = [];
35229         // how many different places could this brick fit horizontally
35230         var groupCount = this.cols + 1 - colSpan;
35231         // for each group potential horizontal position
35232         for ( var i = 0; i < groupCount; i++ ) {
35233           // make an array of colY values for that one group
35234           var groupColYs = this.colYs.slice( i, i + colSpan );
35235           // and get the max value of the array
35236           colGroup[i] = Math.max.apply( Math, groupColYs );
35237         }
35238         return colGroup;
35239     },
35240     /*
35241     _manageStamp : function( stamp )
35242     {
35243         var stampSize =  stamp.getSize();
35244         var offset = stamp.getBox();
35245         // get the columns that this stamp affects
35246         var firstX = this.isOriginLeft ? offset.x : offset.right;
35247         var lastX = firstX + stampSize.width;
35248         var firstCol = Math.floor( firstX / this.columnWidth );
35249         firstCol = Math.max( 0, firstCol );
35250         
35251         var lastCol = Math.floor( lastX / this.columnWidth );
35252         // lastCol should not go over if multiple of columnWidth #425
35253         lastCol -= lastX % this.columnWidth ? 0 : 1;
35254         lastCol = Math.min( this.cols - 1, lastCol );
35255         
35256         // set colYs to bottom of the stamp
35257         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35258             stampSize.height;
35259             
35260         for ( var i = firstCol; i <= lastCol; i++ ) {
35261           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35262         }
35263     },
35264     */
35265     
35266     _getContainerSize : function()
35267     {
35268         this.maxY = Math.max.apply( Math, this.colYs );
35269         var size = {
35270             height: this.maxY
35271         };
35272       
35273         if ( this.isFitWidth ) {
35274             size.width = this._getContainerFitWidth();
35275         }
35276       
35277         return size;
35278     },
35279     
35280     _getContainerFitWidth : function()
35281     {
35282         var unusedCols = 0;
35283         // count unused columns
35284         var i = this.cols;
35285         while ( --i ) {
35286           if ( this.colYs[i] !== 0 ) {
35287             break;
35288           }
35289           unusedCols++;
35290         }
35291         // fit container to columns that have been used
35292         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35293     },
35294     
35295     needsResizeLayout : function()
35296     {
35297         var previousWidth = this.containerWidth;
35298         this.getContainerWidth();
35299         return previousWidth !== this.containerWidth;
35300     }
35301  
35302 });
35303
35304  
35305
35306  /*
35307  * - LGPL
35308  *
35309  * element
35310  * 
35311  */
35312
35313 /**
35314  * @class Roo.bootstrap.MasonryBrick
35315  * @extends Roo.bootstrap.Component
35316  * Bootstrap MasonryBrick class
35317  * 
35318  * @constructor
35319  * Create a new MasonryBrick
35320  * @param {Object} config The config object
35321  */
35322
35323 Roo.bootstrap.MasonryBrick = function(config){
35324     
35325     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35326     
35327     Roo.bootstrap.MasonryBrick.register(this);
35328     
35329     this.addEvents({
35330         // raw events
35331         /**
35332          * @event click
35333          * When a MasonryBrick is clcik
35334          * @param {Roo.bootstrap.MasonryBrick} this
35335          * @param {Roo.EventObject} e
35336          */
35337         "click" : true
35338     });
35339 };
35340
35341 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35342     
35343     /**
35344      * @cfg {String} title
35345      */   
35346     title : '',
35347     /**
35348      * @cfg {String} html
35349      */   
35350     html : '',
35351     /**
35352      * @cfg {String} bgimage
35353      */   
35354     bgimage : '',
35355     /**
35356      * @cfg {String} videourl
35357      */   
35358     videourl : '',
35359     /**
35360      * @cfg {String} cls
35361      */   
35362     cls : '',
35363     /**
35364      * @cfg {String} href
35365      */   
35366     href : '',
35367     /**
35368      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35369      */   
35370     size : 'xs',
35371     
35372     /**
35373      * @cfg {String} placetitle (center|bottom)
35374      */   
35375     placetitle : '',
35376     
35377     /**
35378      * @cfg {Boolean} isFitContainer defalut true
35379      */   
35380     isFitContainer : true, 
35381     
35382     /**
35383      * @cfg {Boolean} preventDefault defalut false
35384      */   
35385     preventDefault : false, 
35386     
35387     /**
35388      * @cfg {Boolean} inverse defalut false
35389      */   
35390     maskInverse : false, 
35391     
35392     getAutoCreate : function()
35393     {
35394         if(!this.isFitContainer){
35395             return this.getSplitAutoCreate();
35396         }
35397         
35398         var cls = 'masonry-brick masonry-brick-full';
35399         
35400         if(this.href.length){
35401             cls += ' masonry-brick-link';
35402         }
35403         
35404         if(this.bgimage.length){
35405             cls += ' masonry-brick-image';
35406         }
35407         
35408         if(this.maskInverse){
35409             cls += ' mask-inverse';
35410         }
35411         
35412         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35413             cls += ' enable-mask';
35414         }
35415         
35416         if(this.size){
35417             cls += ' masonry-' + this.size + '-brick';
35418         }
35419         
35420         if(this.placetitle.length){
35421             
35422             switch (this.placetitle) {
35423                 case 'center' :
35424                     cls += ' masonry-center-title';
35425                     break;
35426                 case 'bottom' :
35427                     cls += ' masonry-bottom-title';
35428                     break;
35429                 default:
35430                     break;
35431             }
35432             
35433         } else {
35434             if(!this.html.length && !this.bgimage.length){
35435                 cls += ' masonry-center-title';
35436             }
35437
35438             if(!this.html.length && this.bgimage.length){
35439                 cls += ' masonry-bottom-title';
35440             }
35441         }
35442         
35443         if(this.cls){
35444             cls += ' ' + this.cls;
35445         }
35446         
35447         var cfg = {
35448             tag: (this.href.length) ? 'a' : 'div',
35449             cls: cls,
35450             cn: [
35451                 {
35452                     tag: 'div',
35453                     cls: 'masonry-brick-mask'
35454                 },
35455                 {
35456                     tag: 'div',
35457                     cls: 'masonry-brick-paragraph',
35458                     cn: []
35459                 }
35460             ]
35461         };
35462         
35463         if(this.href.length){
35464             cfg.href = this.href;
35465         }
35466         
35467         var cn = cfg.cn[1].cn;
35468         
35469         if(this.title.length){
35470             cn.push({
35471                 tag: 'h4',
35472                 cls: 'masonry-brick-title',
35473                 html: this.title
35474             });
35475         }
35476         
35477         if(this.html.length){
35478             cn.push({
35479                 tag: 'p',
35480                 cls: 'masonry-brick-text',
35481                 html: this.html
35482             });
35483         }
35484         
35485         if (!this.title.length && !this.html.length) {
35486             cfg.cn[1].cls += ' hide';
35487         }
35488         
35489         if(this.bgimage.length){
35490             cfg.cn.push({
35491                 tag: 'img',
35492                 cls: 'masonry-brick-image-view',
35493                 src: this.bgimage
35494             });
35495         }
35496         
35497         if(this.videourl.length){
35498             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35499             // youtube support only?
35500             cfg.cn.push({
35501                 tag: 'iframe',
35502                 cls: 'masonry-brick-image-view',
35503                 src: vurl,
35504                 frameborder : 0,
35505                 allowfullscreen : true
35506             });
35507         }
35508         
35509         return cfg;
35510         
35511     },
35512     
35513     getSplitAutoCreate : function()
35514     {
35515         var cls = 'masonry-brick masonry-brick-split';
35516         
35517         if(this.href.length){
35518             cls += ' masonry-brick-link';
35519         }
35520         
35521         if(this.bgimage.length){
35522             cls += ' masonry-brick-image';
35523         }
35524         
35525         if(this.size){
35526             cls += ' masonry-' + this.size + '-brick';
35527         }
35528         
35529         switch (this.placetitle) {
35530             case 'center' :
35531                 cls += ' masonry-center-title';
35532                 break;
35533             case 'bottom' :
35534                 cls += ' masonry-bottom-title';
35535                 break;
35536             default:
35537                 if(!this.bgimage.length){
35538                     cls += ' masonry-center-title';
35539                 }
35540
35541                 if(this.bgimage.length){
35542                     cls += ' masonry-bottom-title';
35543                 }
35544                 break;
35545         }
35546         
35547         if(this.cls){
35548             cls += ' ' + this.cls;
35549         }
35550         
35551         var cfg = {
35552             tag: (this.href.length) ? 'a' : 'div',
35553             cls: cls,
35554             cn: [
35555                 {
35556                     tag: 'div',
35557                     cls: 'masonry-brick-split-head',
35558                     cn: [
35559                         {
35560                             tag: 'div',
35561                             cls: 'masonry-brick-paragraph',
35562                             cn: []
35563                         }
35564                     ]
35565                 },
35566                 {
35567                     tag: 'div',
35568                     cls: 'masonry-brick-split-body',
35569                     cn: []
35570                 }
35571             ]
35572         };
35573         
35574         if(this.href.length){
35575             cfg.href = this.href;
35576         }
35577         
35578         if(this.title.length){
35579             cfg.cn[0].cn[0].cn.push({
35580                 tag: 'h4',
35581                 cls: 'masonry-brick-title',
35582                 html: this.title
35583             });
35584         }
35585         
35586         if(this.html.length){
35587             cfg.cn[1].cn.push({
35588                 tag: 'p',
35589                 cls: 'masonry-brick-text',
35590                 html: this.html
35591             });
35592         }
35593
35594         if(this.bgimage.length){
35595             cfg.cn[0].cn.push({
35596                 tag: 'img',
35597                 cls: 'masonry-brick-image-view',
35598                 src: this.bgimage
35599             });
35600         }
35601         
35602         if(this.videourl.length){
35603             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35604             // youtube support only?
35605             cfg.cn[0].cn.cn.push({
35606                 tag: 'iframe',
35607                 cls: 'masonry-brick-image-view',
35608                 src: vurl,
35609                 frameborder : 0,
35610                 allowfullscreen : true
35611             });
35612         }
35613         
35614         return cfg;
35615     },
35616     
35617     initEvents: function() 
35618     {
35619         switch (this.size) {
35620             case 'xs' :
35621                 this.x = 1;
35622                 this.y = 1;
35623                 break;
35624             case 'sm' :
35625                 this.x = 2;
35626                 this.y = 2;
35627                 break;
35628             case 'md' :
35629             case 'md-left' :
35630             case 'md-right' :
35631                 this.x = 3;
35632                 this.y = 3;
35633                 break;
35634             case 'tall' :
35635                 this.x = 2;
35636                 this.y = 3;
35637                 break;
35638             case 'wide' :
35639                 this.x = 3;
35640                 this.y = 2;
35641                 break;
35642             case 'wide-thin' :
35643                 this.x = 3;
35644                 this.y = 1;
35645                 break;
35646                         
35647             default :
35648                 break;
35649         }
35650         
35651         if(Roo.isTouch){
35652             this.el.on('touchstart', this.onTouchStart, this);
35653             this.el.on('touchmove', this.onTouchMove, this);
35654             this.el.on('touchend', this.onTouchEnd, this);
35655             this.el.on('contextmenu', this.onContextMenu, this);
35656         } else {
35657             this.el.on('mouseenter'  ,this.enter, this);
35658             this.el.on('mouseleave', this.leave, this);
35659             this.el.on('click', this.onClick, this);
35660         }
35661         
35662         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35663             this.parent().bricks.push(this);   
35664         }
35665         
35666     },
35667     
35668     onClick: function(e, el)
35669     {
35670         var time = this.endTimer - this.startTimer;
35671         // Roo.log(e.preventDefault());
35672         if(Roo.isTouch){
35673             if(time > 1000){
35674                 e.preventDefault();
35675                 return;
35676             }
35677         }
35678         
35679         if(!this.preventDefault){
35680             return;
35681         }
35682         
35683         e.preventDefault();
35684         
35685         if (this.activeClass != '') {
35686             this.selectBrick();
35687         }
35688         
35689         this.fireEvent('click', this, e);
35690     },
35691     
35692     enter: function(e, el)
35693     {
35694         e.preventDefault();
35695         
35696         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35697             return;
35698         }
35699         
35700         if(this.bgimage.length && this.html.length){
35701             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35702         }
35703     },
35704     
35705     leave: function(e, el)
35706     {
35707         e.preventDefault();
35708         
35709         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35710             return;
35711         }
35712         
35713         if(this.bgimage.length && this.html.length){
35714             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35715         }
35716     },
35717     
35718     onTouchStart: function(e, el)
35719     {
35720 //        e.preventDefault();
35721         
35722         this.touchmoved = false;
35723         
35724         if(!this.isFitContainer){
35725             return;
35726         }
35727         
35728         if(!this.bgimage.length || !this.html.length){
35729             return;
35730         }
35731         
35732         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35733         
35734         this.timer = new Date().getTime();
35735         
35736     },
35737     
35738     onTouchMove: function(e, el)
35739     {
35740         this.touchmoved = true;
35741     },
35742     
35743     onContextMenu : function(e,el)
35744     {
35745         e.preventDefault();
35746         e.stopPropagation();
35747         return false;
35748     },
35749     
35750     onTouchEnd: function(e, el)
35751     {
35752 //        e.preventDefault();
35753         
35754         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35755         
35756             this.leave(e,el);
35757             
35758             return;
35759         }
35760         
35761         if(!this.bgimage.length || !this.html.length){
35762             
35763             if(this.href.length){
35764                 window.location.href = this.href;
35765             }
35766             
35767             return;
35768         }
35769         
35770         if(!this.isFitContainer){
35771             return;
35772         }
35773         
35774         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35775         
35776         window.location.href = this.href;
35777     },
35778     
35779     //selection on single brick only
35780     selectBrick : function() {
35781         
35782         if (!this.parentId) {
35783             return;
35784         }
35785         
35786         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35787         var index = m.selectedBrick.indexOf(this.id);
35788         
35789         if ( index > -1) {
35790             m.selectedBrick.splice(index,1);
35791             this.el.removeClass(this.activeClass);
35792             return;
35793         }
35794         
35795         for(var i = 0; i < m.selectedBrick.length; i++) {
35796             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35797             b.el.removeClass(b.activeClass);
35798         }
35799         
35800         m.selectedBrick = [];
35801         
35802         m.selectedBrick.push(this.id);
35803         this.el.addClass(this.activeClass);
35804         return;
35805     },
35806     
35807     isSelected : function(){
35808         return this.el.hasClass(this.activeClass);
35809         
35810     }
35811 });
35812
35813 Roo.apply(Roo.bootstrap.MasonryBrick, {
35814     
35815     //groups: {},
35816     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35817      /**
35818     * register a Masonry Brick
35819     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35820     */
35821     
35822     register : function(brick)
35823     {
35824         //this.groups[brick.id] = brick;
35825         this.groups.add(brick.id, brick);
35826     },
35827     /**
35828     * fetch a  masonry brick based on the masonry brick ID
35829     * @param {string} the masonry brick to add
35830     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35831     */
35832     
35833     get: function(brick_id) 
35834     {
35835         // if (typeof(this.groups[brick_id]) == 'undefined') {
35836         //     return false;
35837         // }
35838         // return this.groups[brick_id] ;
35839         
35840         if(this.groups.key(brick_id)) {
35841             return this.groups.key(brick_id);
35842         }
35843         
35844         return false;
35845     }
35846     
35847     
35848     
35849 });
35850
35851  /*
35852  * - LGPL
35853  *
35854  * element
35855  * 
35856  */
35857
35858 /**
35859  * @class Roo.bootstrap.Brick
35860  * @extends Roo.bootstrap.Component
35861  * Bootstrap Brick class
35862  * 
35863  * @constructor
35864  * Create a new Brick
35865  * @param {Object} config The config object
35866  */
35867
35868 Roo.bootstrap.Brick = function(config){
35869     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35870     
35871     this.addEvents({
35872         // raw events
35873         /**
35874          * @event click
35875          * When a Brick is click
35876          * @param {Roo.bootstrap.Brick} this
35877          * @param {Roo.EventObject} e
35878          */
35879         "click" : true
35880     });
35881 };
35882
35883 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35884     
35885     /**
35886      * @cfg {String} title
35887      */   
35888     title : '',
35889     /**
35890      * @cfg {String} html
35891      */   
35892     html : '',
35893     /**
35894      * @cfg {String} bgimage
35895      */   
35896     bgimage : '',
35897     /**
35898      * @cfg {String} cls
35899      */   
35900     cls : '',
35901     /**
35902      * @cfg {String} href
35903      */   
35904     href : '',
35905     /**
35906      * @cfg {String} video
35907      */   
35908     video : '',
35909     /**
35910      * @cfg {Boolean} square
35911      */   
35912     square : true,
35913     
35914     getAutoCreate : function()
35915     {
35916         var cls = 'roo-brick';
35917         
35918         if(this.href.length){
35919             cls += ' roo-brick-link';
35920         }
35921         
35922         if(this.bgimage.length){
35923             cls += ' roo-brick-image';
35924         }
35925         
35926         if(!this.html.length && !this.bgimage.length){
35927             cls += ' roo-brick-center-title';
35928         }
35929         
35930         if(!this.html.length && this.bgimage.length){
35931             cls += ' roo-brick-bottom-title';
35932         }
35933         
35934         if(this.cls){
35935             cls += ' ' + this.cls;
35936         }
35937         
35938         var cfg = {
35939             tag: (this.href.length) ? 'a' : 'div',
35940             cls: cls,
35941             cn: [
35942                 {
35943                     tag: 'div',
35944                     cls: 'roo-brick-paragraph',
35945                     cn: []
35946                 }
35947             ]
35948         };
35949         
35950         if(this.href.length){
35951             cfg.href = this.href;
35952         }
35953         
35954         var cn = cfg.cn[0].cn;
35955         
35956         if(this.title.length){
35957             cn.push({
35958                 tag: 'h4',
35959                 cls: 'roo-brick-title',
35960                 html: this.title
35961             });
35962         }
35963         
35964         if(this.html.length){
35965             cn.push({
35966                 tag: 'p',
35967                 cls: 'roo-brick-text',
35968                 html: this.html
35969             });
35970         } else {
35971             cn.cls += ' hide';
35972         }
35973         
35974         if(this.bgimage.length){
35975             cfg.cn.push({
35976                 tag: 'img',
35977                 cls: 'roo-brick-image-view',
35978                 src: this.bgimage
35979             });
35980         }
35981         
35982         return cfg;
35983     },
35984     
35985     initEvents: function() 
35986     {
35987         if(this.title.length || this.html.length){
35988             this.el.on('mouseenter'  ,this.enter, this);
35989             this.el.on('mouseleave', this.leave, this);
35990         }
35991         
35992         Roo.EventManager.onWindowResize(this.resize, this); 
35993         
35994         if(this.bgimage.length){
35995             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35996             this.imageEl.on('load', this.onImageLoad, this);
35997             return;
35998         }
35999         
36000         this.resize();
36001     },
36002     
36003     onImageLoad : function()
36004     {
36005         this.resize();
36006     },
36007     
36008     resize : function()
36009     {
36010         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36011         
36012         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36013         
36014         if(this.bgimage.length){
36015             var image = this.el.select('.roo-brick-image-view', true).first();
36016             
36017             image.setWidth(paragraph.getWidth());
36018             
36019             if(this.square){
36020                 image.setHeight(paragraph.getWidth());
36021             }
36022             
36023             this.el.setHeight(image.getHeight());
36024             paragraph.setHeight(image.getHeight());
36025             
36026         }
36027         
36028     },
36029     
36030     enter: function(e, el)
36031     {
36032         e.preventDefault();
36033         
36034         if(this.bgimage.length){
36035             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36036             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36037         }
36038     },
36039     
36040     leave: function(e, el)
36041     {
36042         e.preventDefault();
36043         
36044         if(this.bgimage.length){
36045             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36046             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36047         }
36048     }
36049     
36050 });
36051
36052  
36053
36054  /*
36055  * - LGPL
36056  *
36057  * Number field 
36058  */
36059
36060 /**
36061  * @class Roo.bootstrap.NumberField
36062  * @extends Roo.bootstrap.Input
36063  * Bootstrap NumberField class
36064  * 
36065  * 
36066  * 
36067  * 
36068  * @constructor
36069  * Create a new NumberField
36070  * @param {Object} config The config object
36071  */
36072
36073 Roo.bootstrap.NumberField = function(config){
36074     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36075 };
36076
36077 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36078     
36079     /**
36080      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36081      */
36082     allowDecimals : true,
36083     /**
36084      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36085      */
36086     decimalSeparator : ".",
36087     /**
36088      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36089      */
36090     decimalPrecision : 2,
36091     /**
36092      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36093      */
36094     allowNegative : true,
36095     
36096     /**
36097      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36098      */
36099     allowZero: true,
36100     /**
36101      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36102      */
36103     minValue : Number.NEGATIVE_INFINITY,
36104     /**
36105      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36106      */
36107     maxValue : Number.MAX_VALUE,
36108     /**
36109      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36110      */
36111     minText : "The minimum value for this field is {0}",
36112     /**
36113      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36114      */
36115     maxText : "The maximum value for this field is {0}",
36116     /**
36117      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36118      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36119      */
36120     nanText : "{0} is not a valid number",
36121     /**
36122      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36123      */
36124     thousandsDelimiter : false,
36125     /**
36126      * @cfg {String} valueAlign alignment of value
36127      */
36128     valueAlign : "left",
36129
36130     getAutoCreate : function()
36131     {
36132         var hiddenInput = {
36133             tag: 'input',
36134             type: 'hidden',
36135             id: Roo.id(),
36136             cls: 'hidden-number-input'
36137         };
36138         
36139         if (this.name) {
36140             hiddenInput.name = this.name;
36141         }
36142         
36143         this.name = '';
36144         
36145         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36146         
36147         this.name = hiddenInput.name;
36148         
36149         if(cfg.cn.length > 0) {
36150             cfg.cn.push(hiddenInput);
36151         }
36152         
36153         return cfg;
36154     },
36155
36156     // private
36157     initEvents : function()
36158     {   
36159         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36160         
36161         var allowed = "0123456789";
36162         
36163         if(this.allowDecimals){
36164             allowed += this.decimalSeparator;
36165         }
36166         
36167         if(this.allowNegative){
36168             allowed += "-";
36169         }
36170         
36171         if(this.thousandsDelimiter) {
36172             allowed += ",";
36173         }
36174         
36175         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36176         
36177         var keyPress = function(e){
36178             
36179             var k = e.getKey();
36180             
36181             var c = e.getCharCode();
36182             
36183             if(
36184                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36185                     allowed.indexOf(String.fromCharCode(c)) === -1
36186             ){
36187                 e.stopEvent();
36188                 return;
36189             }
36190             
36191             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36192                 return;
36193             }
36194             
36195             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36196                 e.stopEvent();
36197             }
36198         };
36199         
36200         this.el.on("keypress", keyPress, this);
36201     },
36202     
36203     validateValue : function(value)
36204     {
36205         
36206         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36207             return false;
36208         }
36209         
36210         var num = this.parseValue(value);
36211         
36212         if(isNaN(num)){
36213             this.markInvalid(String.format(this.nanText, value));
36214             return false;
36215         }
36216         
36217         if(num < this.minValue){
36218             this.markInvalid(String.format(this.minText, this.minValue));
36219             return false;
36220         }
36221         
36222         if(num > this.maxValue){
36223             this.markInvalid(String.format(this.maxText, this.maxValue));
36224             return false;
36225         }
36226         
36227         return true;
36228     },
36229
36230     getValue : function()
36231     {
36232         var v = this.hiddenEl().getValue();
36233         
36234         return this.fixPrecision(this.parseValue(v));
36235     },
36236
36237     parseValue : function(value)
36238     {
36239         if(this.thousandsDelimiter) {
36240             value += "";
36241             r = new RegExp(",", "g");
36242             value = value.replace(r, "");
36243         }
36244         
36245         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36246         return isNaN(value) ? '' : value;
36247     },
36248
36249     fixPrecision : function(value)
36250     {
36251         if(this.thousandsDelimiter) {
36252             value += "";
36253             r = new RegExp(",", "g");
36254             value = value.replace(r, "");
36255         }
36256         
36257         var nan = isNaN(value);
36258         
36259         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36260             return nan ? '' : value;
36261         }
36262         return parseFloat(value).toFixed(this.decimalPrecision);
36263     },
36264
36265     setValue : function(v)
36266     {
36267         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36268         
36269         this.value = v;
36270         
36271         if(this.rendered){
36272             
36273             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36274             
36275             this.inputEl().dom.value = (v == '') ? '' :
36276                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36277             
36278             if(!this.allowZero && v === '0') {
36279                 this.hiddenEl().dom.value = '';
36280                 this.inputEl().dom.value = '';
36281             }
36282             
36283             this.validate();
36284         }
36285     },
36286
36287     decimalPrecisionFcn : function(v)
36288     {
36289         return Math.floor(v);
36290     },
36291
36292     beforeBlur : function()
36293     {
36294         var v = this.parseValue(this.getRawValue());
36295         
36296         if(v || v === 0 || v === ''){
36297             this.setValue(v);
36298         }
36299     },
36300     
36301     hiddenEl : function()
36302     {
36303         return this.el.select('input.hidden-number-input',true).first();
36304     }
36305     
36306 });
36307
36308  
36309
36310 /*
36311 * Licence: LGPL
36312 */
36313
36314 /**
36315  * @class Roo.bootstrap.DocumentSlider
36316  * @extends Roo.bootstrap.Component
36317  * Bootstrap DocumentSlider class
36318  * 
36319  * @constructor
36320  * Create a new DocumentViewer
36321  * @param {Object} config The config object
36322  */
36323
36324 Roo.bootstrap.DocumentSlider = function(config){
36325     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36326     
36327     this.files = [];
36328     
36329     this.addEvents({
36330         /**
36331          * @event initial
36332          * Fire after initEvent
36333          * @param {Roo.bootstrap.DocumentSlider} this
36334          */
36335         "initial" : true,
36336         /**
36337          * @event update
36338          * Fire after update
36339          * @param {Roo.bootstrap.DocumentSlider} this
36340          */
36341         "update" : true,
36342         /**
36343          * @event click
36344          * Fire after click
36345          * @param {Roo.bootstrap.DocumentSlider} this
36346          */
36347         "click" : true
36348     });
36349 };
36350
36351 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36352     
36353     files : false,
36354     
36355     indicator : 0,
36356     
36357     getAutoCreate : function()
36358     {
36359         var cfg = {
36360             tag : 'div',
36361             cls : 'roo-document-slider',
36362             cn : [
36363                 {
36364                     tag : 'div',
36365                     cls : 'roo-document-slider-header',
36366                     cn : [
36367                         {
36368                             tag : 'div',
36369                             cls : 'roo-document-slider-header-title'
36370                         }
36371                     ]
36372                 },
36373                 {
36374                     tag : 'div',
36375                     cls : 'roo-document-slider-body',
36376                     cn : [
36377                         {
36378                             tag : 'div',
36379                             cls : 'roo-document-slider-prev',
36380                             cn : [
36381                                 {
36382                                     tag : 'i',
36383                                     cls : 'fa fa-chevron-left'
36384                                 }
36385                             ]
36386                         },
36387                         {
36388                             tag : 'div',
36389                             cls : 'roo-document-slider-thumb',
36390                             cn : [
36391                                 {
36392                                     tag : 'img',
36393                                     cls : 'roo-document-slider-image'
36394                                 }
36395                             ]
36396                         },
36397                         {
36398                             tag : 'div',
36399                             cls : 'roo-document-slider-next',
36400                             cn : [
36401                                 {
36402                                     tag : 'i',
36403                                     cls : 'fa fa-chevron-right'
36404                                 }
36405                             ]
36406                         }
36407                     ]
36408                 }
36409             ]
36410         };
36411         
36412         return cfg;
36413     },
36414     
36415     initEvents : function()
36416     {
36417         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36418         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36419         
36420         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36421         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36422         
36423         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36424         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36425         
36426         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36427         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36428         
36429         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36430         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36431         
36432         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36433         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36434         
36435         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36436         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36437         
36438         this.thumbEl.on('click', this.onClick, this);
36439         
36440         this.prevIndicator.on('click', this.prev, this);
36441         
36442         this.nextIndicator.on('click', this.next, this);
36443         
36444     },
36445     
36446     initial : function()
36447     {
36448         if(this.files.length){
36449             this.indicator = 1;
36450             this.update()
36451         }
36452         
36453         this.fireEvent('initial', this);
36454     },
36455     
36456     update : function()
36457     {
36458         this.imageEl.attr('src', this.files[this.indicator - 1]);
36459         
36460         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36461         
36462         this.prevIndicator.show();
36463         
36464         if(this.indicator == 1){
36465             this.prevIndicator.hide();
36466         }
36467         
36468         this.nextIndicator.show();
36469         
36470         if(this.indicator == this.files.length){
36471             this.nextIndicator.hide();
36472         }
36473         
36474         this.thumbEl.scrollTo('top');
36475         
36476         this.fireEvent('update', this);
36477     },
36478     
36479     onClick : function(e)
36480     {
36481         e.preventDefault();
36482         
36483         this.fireEvent('click', this);
36484     },
36485     
36486     prev : function(e)
36487     {
36488         e.preventDefault();
36489         
36490         this.indicator = Math.max(1, this.indicator - 1);
36491         
36492         this.update();
36493     },
36494     
36495     next : function(e)
36496     {
36497         e.preventDefault();
36498         
36499         this.indicator = Math.min(this.files.length, this.indicator + 1);
36500         
36501         this.update();
36502     }
36503 });
36504 /*
36505  * - LGPL
36506  *
36507  * RadioSet
36508  *
36509  *
36510  */
36511
36512 /**
36513  * @class Roo.bootstrap.RadioSet
36514  * @extends Roo.bootstrap.Input
36515  * Bootstrap RadioSet class
36516  * @cfg {String} indicatorpos (left|right) default left
36517  * @cfg {Boolean} inline (true|false) inline the element (default true)
36518  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36519  * @constructor
36520  * Create a new RadioSet
36521  * @param {Object} config The config object
36522  */
36523
36524 Roo.bootstrap.RadioSet = function(config){
36525     
36526     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36527     
36528     this.radioes = [];
36529     
36530     Roo.bootstrap.RadioSet.register(this);
36531     
36532     this.addEvents({
36533         /**
36534         * @event check
36535         * Fires when the element is checked or unchecked.
36536         * @param {Roo.bootstrap.RadioSet} this This radio
36537         * @param {Roo.bootstrap.Radio} item The checked item
36538         */
36539        check : true,
36540        /**
36541         * @event click
36542         * Fires when the element is click.
36543         * @param {Roo.bootstrap.RadioSet} this This radio set
36544         * @param {Roo.bootstrap.Radio} item The checked item
36545         * @param {Roo.EventObject} e The event object
36546         */
36547        click : true
36548     });
36549     
36550 };
36551
36552 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36553
36554     radioes : false,
36555     
36556     inline : true,
36557     
36558     weight : '',
36559     
36560     indicatorpos : 'left',
36561     
36562     getAutoCreate : function()
36563     {
36564         var label = {
36565             tag : 'label',
36566             cls : 'roo-radio-set-label',
36567             cn : [
36568                 {
36569                     tag : 'span',
36570                     html : this.fieldLabel
36571                 }
36572             ]
36573         };
36574         if (Roo.bootstrap.version == 3) {
36575             
36576             
36577             if(this.indicatorpos == 'left'){
36578                 label.cn.unshift({
36579                     tag : 'i',
36580                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36581                     tooltip : 'This field is required'
36582                 });
36583             } else {
36584                 label.cn.push({
36585                     tag : 'i',
36586                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36587                     tooltip : 'This field is required'
36588                 });
36589             }
36590         }
36591         var items = {
36592             tag : 'div',
36593             cls : 'roo-radio-set-items'
36594         };
36595         
36596         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36597         
36598         if (align === 'left' && this.fieldLabel.length) {
36599             
36600             items = {
36601                 cls : "roo-radio-set-right", 
36602                 cn: [
36603                     items
36604                 ]
36605             };
36606             
36607             if(this.labelWidth > 12){
36608                 label.style = "width: " + this.labelWidth + 'px';
36609             }
36610             
36611             if(this.labelWidth < 13 && this.labelmd == 0){
36612                 this.labelmd = this.labelWidth;
36613             }
36614             
36615             if(this.labellg > 0){
36616                 label.cls += ' col-lg-' + this.labellg;
36617                 items.cls += ' col-lg-' + (12 - this.labellg);
36618             }
36619             
36620             if(this.labelmd > 0){
36621                 label.cls += ' col-md-' + this.labelmd;
36622                 items.cls += ' col-md-' + (12 - this.labelmd);
36623             }
36624             
36625             if(this.labelsm > 0){
36626                 label.cls += ' col-sm-' + this.labelsm;
36627                 items.cls += ' col-sm-' + (12 - this.labelsm);
36628             }
36629             
36630             if(this.labelxs > 0){
36631                 label.cls += ' col-xs-' + this.labelxs;
36632                 items.cls += ' col-xs-' + (12 - this.labelxs);
36633             }
36634         }
36635         
36636         var cfg = {
36637             tag : 'div',
36638             cls : 'roo-radio-set',
36639             cn : [
36640                 {
36641                     tag : 'input',
36642                     cls : 'roo-radio-set-input',
36643                     type : 'hidden',
36644                     name : this.name,
36645                     value : this.value ? this.value :  ''
36646                 },
36647                 label,
36648                 items
36649             ]
36650         };
36651         
36652         if(this.weight.length){
36653             cfg.cls += ' roo-radio-' + this.weight;
36654         }
36655         
36656         if(this.inline) {
36657             cfg.cls += ' roo-radio-set-inline';
36658         }
36659         
36660         var settings=this;
36661         ['xs','sm','md','lg'].map(function(size){
36662             if (settings[size]) {
36663                 cfg.cls += ' col-' + size + '-' + settings[size];
36664             }
36665         });
36666         
36667         return cfg;
36668         
36669     },
36670
36671     initEvents : function()
36672     {
36673         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36674         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36675         
36676         if(!this.fieldLabel.length){
36677             this.labelEl.hide();
36678         }
36679         
36680         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36681         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36682         
36683         this.indicator = this.indicatorEl();
36684         
36685         if(this.indicator){
36686             this.indicator.addClass('invisible');
36687         }
36688         
36689         this.originalValue = this.getValue();
36690         
36691     },
36692     
36693     inputEl: function ()
36694     {
36695         return this.el.select('.roo-radio-set-input', true).first();
36696     },
36697     
36698     getChildContainer : function()
36699     {
36700         return this.itemsEl;
36701     },
36702     
36703     register : function(item)
36704     {
36705         this.radioes.push(item);
36706         
36707     },
36708     
36709     validate : function()
36710     {   
36711         if(this.getVisibilityEl().hasClass('hidden')){
36712             return true;
36713         }
36714         
36715         var valid = false;
36716         
36717         Roo.each(this.radioes, function(i){
36718             if(!i.checked){
36719                 return;
36720             }
36721             
36722             valid = true;
36723             return false;
36724         });
36725         
36726         if(this.allowBlank) {
36727             return true;
36728         }
36729         
36730         if(this.disabled || valid){
36731             this.markValid();
36732             return true;
36733         }
36734         
36735         this.markInvalid();
36736         return false;
36737         
36738     },
36739     
36740     markValid : function()
36741     {
36742         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36743             this.indicatorEl().removeClass('visible');
36744             this.indicatorEl().addClass('invisible');
36745         }
36746         
36747         
36748         if (Roo.bootstrap.version == 3) {
36749             this.el.removeClass([this.invalidClass, this.validClass]);
36750             this.el.addClass(this.validClass);
36751         } else {
36752             this.el.removeClass(['is-invalid','is-valid']);
36753             this.el.addClass(['is-valid']);
36754         }
36755         this.fireEvent('valid', this);
36756     },
36757     
36758     markInvalid : function(msg)
36759     {
36760         if(this.allowBlank || this.disabled){
36761             return;
36762         }
36763         
36764         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36765             this.indicatorEl().removeClass('invisible');
36766             this.indicatorEl().addClass('visible');
36767         }
36768         if (Roo.bootstrap.version == 3) {
36769             this.el.removeClass([this.invalidClass, this.validClass]);
36770             this.el.addClass(this.invalidClass);
36771         } else {
36772             this.el.removeClass(['is-invalid','is-valid']);
36773             this.el.addClass(['is-invalid']);
36774         }
36775         
36776         this.fireEvent('invalid', this, msg);
36777         
36778     },
36779     
36780     setValue : function(v, suppressEvent)
36781     {   
36782         if(this.value === v){
36783             return;
36784         }
36785         
36786         this.value = v;
36787         
36788         if(this.rendered){
36789             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36790         }
36791         
36792         Roo.each(this.radioes, function(i){
36793             i.checked = false;
36794             i.el.removeClass('checked');
36795         });
36796         
36797         Roo.each(this.radioes, function(i){
36798             
36799             if(i.value === v || i.value.toString() === v.toString()){
36800                 i.checked = true;
36801                 i.el.addClass('checked');
36802                 
36803                 if(suppressEvent !== true){
36804                     this.fireEvent('check', this, i);
36805                 }
36806                 
36807                 return false;
36808             }
36809             
36810         }, this);
36811         
36812         this.validate();
36813     },
36814     
36815     clearInvalid : function(){
36816         
36817         if(!this.el || this.preventMark){
36818             return;
36819         }
36820         
36821         this.el.removeClass([this.invalidClass]);
36822         
36823         this.fireEvent('valid', this);
36824     }
36825     
36826 });
36827
36828 Roo.apply(Roo.bootstrap.RadioSet, {
36829     
36830     groups: {},
36831     
36832     register : function(set)
36833     {
36834         this.groups[set.name] = set;
36835     },
36836     
36837     get: function(name) 
36838     {
36839         if (typeof(this.groups[name]) == 'undefined') {
36840             return false;
36841         }
36842         
36843         return this.groups[name] ;
36844     }
36845     
36846 });
36847 /*
36848  * Based on:
36849  * Ext JS Library 1.1.1
36850  * Copyright(c) 2006-2007, Ext JS, LLC.
36851  *
36852  * Originally Released Under LGPL - original licence link has changed is not relivant.
36853  *
36854  * Fork - LGPL
36855  * <script type="text/javascript">
36856  */
36857
36858
36859 /**
36860  * @class Roo.bootstrap.SplitBar
36861  * @extends Roo.util.Observable
36862  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36863  * <br><br>
36864  * Usage:
36865  * <pre><code>
36866 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36867                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36868 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36869 split.minSize = 100;
36870 split.maxSize = 600;
36871 split.animate = true;
36872 split.on('moved', splitterMoved);
36873 </code></pre>
36874  * @constructor
36875  * Create a new SplitBar
36876  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36877  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36878  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36879  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36880                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36881                         position of the SplitBar).
36882  */
36883 Roo.bootstrap.SplitBar = function(cfg){
36884     
36885     /** @private */
36886     
36887     //{
36888     //  dragElement : elm
36889     //  resizingElement: el,
36890         // optional..
36891     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36892     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36893         // existingProxy ???
36894     //}
36895     
36896     this.el = Roo.get(cfg.dragElement, true);
36897     this.el.dom.unselectable = "on";
36898     /** @private */
36899     this.resizingEl = Roo.get(cfg.resizingElement, true);
36900
36901     /**
36902      * @private
36903      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36904      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36905      * @type Number
36906      */
36907     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36908     
36909     /**
36910      * The minimum size of the resizing element. (Defaults to 0)
36911      * @type Number
36912      */
36913     this.minSize = 0;
36914     
36915     /**
36916      * The maximum size of the resizing element. (Defaults to 2000)
36917      * @type Number
36918      */
36919     this.maxSize = 2000;
36920     
36921     /**
36922      * Whether to animate the transition to the new size
36923      * @type Boolean
36924      */
36925     this.animate = false;
36926     
36927     /**
36928      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36929      * @type Boolean
36930      */
36931     this.useShim = false;
36932     
36933     /** @private */
36934     this.shim = null;
36935     
36936     if(!cfg.existingProxy){
36937         /** @private */
36938         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36939     }else{
36940         this.proxy = Roo.get(cfg.existingProxy).dom;
36941     }
36942     /** @private */
36943     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36944     
36945     /** @private */
36946     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36947     
36948     /** @private */
36949     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36950     
36951     /** @private */
36952     this.dragSpecs = {};
36953     
36954     /**
36955      * @private The adapter to use to positon and resize elements
36956      */
36957     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36958     this.adapter.init(this);
36959     
36960     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36961         /** @private */
36962         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36963         this.el.addClass("roo-splitbar-h");
36964     }else{
36965         /** @private */
36966         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36967         this.el.addClass("roo-splitbar-v");
36968     }
36969     
36970     this.addEvents({
36971         /**
36972          * @event resize
36973          * Fires when the splitter is moved (alias for {@link #event-moved})
36974          * @param {Roo.bootstrap.SplitBar} this
36975          * @param {Number} newSize the new width or height
36976          */
36977         "resize" : true,
36978         /**
36979          * @event moved
36980          * Fires when the splitter is moved
36981          * @param {Roo.bootstrap.SplitBar} this
36982          * @param {Number} newSize the new width or height
36983          */
36984         "moved" : true,
36985         /**
36986          * @event beforeresize
36987          * Fires before the splitter is dragged
36988          * @param {Roo.bootstrap.SplitBar} this
36989          */
36990         "beforeresize" : true,
36991
36992         "beforeapply" : true
36993     });
36994
36995     Roo.util.Observable.call(this);
36996 };
36997
36998 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36999     onStartProxyDrag : function(x, y){
37000         this.fireEvent("beforeresize", this);
37001         if(!this.overlay){
37002             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37003             o.unselectable();
37004             o.enableDisplayMode("block");
37005             // all splitbars share the same overlay
37006             Roo.bootstrap.SplitBar.prototype.overlay = o;
37007         }
37008         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37009         this.overlay.show();
37010         Roo.get(this.proxy).setDisplayed("block");
37011         var size = this.adapter.getElementSize(this);
37012         this.activeMinSize = this.getMinimumSize();;
37013         this.activeMaxSize = this.getMaximumSize();;
37014         var c1 = size - this.activeMinSize;
37015         var c2 = Math.max(this.activeMaxSize - size, 0);
37016         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37017             this.dd.resetConstraints();
37018             this.dd.setXConstraint(
37019                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37020                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37021             );
37022             this.dd.setYConstraint(0, 0);
37023         }else{
37024             this.dd.resetConstraints();
37025             this.dd.setXConstraint(0, 0);
37026             this.dd.setYConstraint(
37027                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37028                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37029             );
37030          }
37031         this.dragSpecs.startSize = size;
37032         this.dragSpecs.startPoint = [x, y];
37033         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37034     },
37035     
37036     /** 
37037      * @private Called after the drag operation by the DDProxy
37038      */
37039     onEndProxyDrag : function(e){
37040         Roo.get(this.proxy).setDisplayed(false);
37041         var endPoint = Roo.lib.Event.getXY(e);
37042         if(this.overlay){
37043             this.overlay.hide();
37044         }
37045         var newSize;
37046         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37047             newSize = this.dragSpecs.startSize + 
37048                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37049                     endPoint[0] - this.dragSpecs.startPoint[0] :
37050                     this.dragSpecs.startPoint[0] - endPoint[0]
37051                 );
37052         }else{
37053             newSize = this.dragSpecs.startSize + 
37054                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37055                     endPoint[1] - this.dragSpecs.startPoint[1] :
37056                     this.dragSpecs.startPoint[1] - endPoint[1]
37057                 );
37058         }
37059         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37060         if(newSize != this.dragSpecs.startSize){
37061             if(this.fireEvent('beforeapply', this, newSize) !== false){
37062                 this.adapter.setElementSize(this, newSize);
37063                 this.fireEvent("moved", this, newSize);
37064                 this.fireEvent("resize", this, newSize);
37065             }
37066         }
37067     },
37068     
37069     /**
37070      * Get the adapter this SplitBar uses
37071      * @return The adapter object
37072      */
37073     getAdapter : function(){
37074         return this.adapter;
37075     },
37076     
37077     /**
37078      * Set the adapter this SplitBar uses
37079      * @param {Object} adapter A SplitBar adapter object
37080      */
37081     setAdapter : function(adapter){
37082         this.adapter = adapter;
37083         this.adapter.init(this);
37084     },
37085     
37086     /**
37087      * Gets the minimum size for the resizing element
37088      * @return {Number} The minimum size
37089      */
37090     getMinimumSize : function(){
37091         return this.minSize;
37092     },
37093     
37094     /**
37095      * Sets the minimum size for the resizing element
37096      * @param {Number} minSize The minimum size
37097      */
37098     setMinimumSize : function(minSize){
37099         this.minSize = minSize;
37100     },
37101     
37102     /**
37103      * Gets the maximum size for the resizing element
37104      * @return {Number} The maximum size
37105      */
37106     getMaximumSize : function(){
37107         return this.maxSize;
37108     },
37109     
37110     /**
37111      * Sets the maximum size for the resizing element
37112      * @param {Number} maxSize The maximum size
37113      */
37114     setMaximumSize : function(maxSize){
37115         this.maxSize = maxSize;
37116     },
37117     
37118     /**
37119      * Sets the initialize size for the resizing element
37120      * @param {Number} size The initial size
37121      */
37122     setCurrentSize : function(size){
37123         var oldAnimate = this.animate;
37124         this.animate = false;
37125         this.adapter.setElementSize(this, size);
37126         this.animate = oldAnimate;
37127     },
37128     
37129     /**
37130      * Destroy this splitbar. 
37131      * @param {Boolean} removeEl True to remove the element
37132      */
37133     destroy : function(removeEl){
37134         if(this.shim){
37135             this.shim.remove();
37136         }
37137         this.dd.unreg();
37138         this.proxy.parentNode.removeChild(this.proxy);
37139         if(removeEl){
37140             this.el.remove();
37141         }
37142     }
37143 });
37144
37145 /**
37146  * @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.
37147  */
37148 Roo.bootstrap.SplitBar.createProxy = function(dir){
37149     var proxy = new Roo.Element(document.createElement("div"));
37150     proxy.unselectable();
37151     var cls = 'roo-splitbar-proxy';
37152     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37153     document.body.appendChild(proxy.dom);
37154     return proxy.dom;
37155 };
37156
37157 /** 
37158  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37159  * Default Adapter. It assumes the splitter and resizing element are not positioned
37160  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37161  */
37162 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37163 };
37164
37165 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37166     // do nothing for now
37167     init : function(s){
37168     
37169     },
37170     /**
37171      * Called before drag operations to get the current size of the resizing element. 
37172      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37173      */
37174      getElementSize : function(s){
37175         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37176             return s.resizingEl.getWidth();
37177         }else{
37178             return s.resizingEl.getHeight();
37179         }
37180     },
37181     
37182     /**
37183      * Called after drag operations to set the size of the resizing element.
37184      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37185      * @param {Number} newSize The new size to set
37186      * @param {Function} onComplete A function to be invoked when resizing is complete
37187      */
37188     setElementSize : function(s, newSize, onComplete){
37189         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37190             if(!s.animate){
37191                 s.resizingEl.setWidth(newSize);
37192                 if(onComplete){
37193                     onComplete(s, newSize);
37194                 }
37195             }else{
37196                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37197             }
37198         }else{
37199             
37200             if(!s.animate){
37201                 s.resizingEl.setHeight(newSize);
37202                 if(onComplete){
37203                     onComplete(s, newSize);
37204                 }
37205             }else{
37206                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37207             }
37208         }
37209     }
37210 };
37211
37212 /** 
37213  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37214  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37215  * Adapter that  moves the splitter element to align with the resized sizing element. 
37216  * Used with an absolute positioned SplitBar.
37217  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37218  * document.body, make sure you assign an id to the body element.
37219  */
37220 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37221     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37222     this.container = Roo.get(container);
37223 };
37224
37225 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37226     init : function(s){
37227         this.basic.init(s);
37228     },
37229     
37230     getElementSize : function(s){
37231         return this.basic.getElementSize(s);
37232     },
37233     
37234     setElementSize : function(s, newSize, onComplete){
37235         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37236     },
37237     
37238     moveSplitter : function(s){
37239         var yes = Roo.bootstrap.SplitBar;
37240         switch(s.placement){
37241             case yes.LEFT:
37242                 s.el.setX(s.resizingEl.getRight());
37243                 break;
37244             case yes.RIGHT:
37245                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37246                 break;
37247             case yes.TOP:
37248                 s.el.setY(s.resizingEl.getBottom());
37249                 break;
37250             case yes.BOTTOM:
37251                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37252                 break;
37253         }
37254     }
37255 };
37256
37257 /**
37258  * Orientation constant - Create a vertical SplitBar
37259  * @static
37260  * @type Number
37261  */
37262 Roo.bootstrap.SplitBar.VERTICAL = 1;
37263
37264 /**
37265  * Orientation constant - Create a horizontal SplitBar
37266  * @static
37267  * @type Number
37268  */
37269 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37270
37271 /**
37272  * Placement constant - The resizing element is to the left of the splitter element
37273  * @static
37274  * @type Number
37275  */
37276 Roo.bootstrap.SplitBar.LEFT = 1;
37277
37278 /**
37279  * Placement constant - The resizing element is to the right of the splitter element
37280  * @static
37281  * @type Number
37282  */
37283 Roo.bootstrap.SplitBar.RIGHT = 2;
37284
37285 /**
37286  * Placement constant - The resizing element is positioned above the splitter element
37287  * @static
37288  * @type Number
37289  */
37290 Roo.bootstrap.SplitBar.TOP = 3;
37291
37292 /**
37293  * Placement constant - The resizing element is positioned under splitter element
37294  * @static
37295  * @type Number
37296  */
37297 Roo.bootstrap.SplitBar.BOTTOM = 4;
37298 Roo.namespace("Roo.bootstrap.layout");/*
37299  * Based on:
37300  * Ext JS Library 1.1.1
37301  * Copyright(c) 2006-2007, Ext JS, LLC.
37302  *
37303  * Originally Released Under LGPL - original licence link has changed is not relivant.
37304  *
37305  * Fork - LGPL
37306  * <script type="text/javascript">
37307  */
37308
37309 /**
37310  * @class Roo.bootstrap.layout.Manager
37311  * @extends Roo.bootstrap.Component
37312  * Base class for layout managers.
37313  */
37314 Roo.bootstrap.layout.Manager = function(config)
37315 {
37316     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37317
37318
37319
37320
37321
37322     /** false to disable window resize monitoring @type Boolean */
37323     this.monitorWindowResize = true;
37324     this.regions = {};
37325     this.addEvents({
37326         /**
37327          * @event layout
37328          * Fires when a layout is performed.
37329          * @param {Roo.LayoutManager} this
37330          */
37331         "layout" : true,
37332         /**
37333          * @event regionresized
37334          * Fires when the user resizes a region.
37335          * @param {Roo.LayoutRegion} region The resized region
37336          * @param {Number} newSize The new size (width for east/west, height for north/south)
37337          */
37338         "regionresized" : true,
37339         /**
37340          * @event regioncollapsed
37341          * Fires when a region is collapsed.
37342          * @param {Roo.LayoutRegion} region The collapsed region
37343          */
37344         "regioncollapsed" : true,
37345         /**
37346          * @event regionexpanded
37347          * Fires when a region is expanded.
37348          * @param {Roo.LayoutRegion} region The expanded region
37349          */
37350         "regionexpanded" : true
37351     });
37352     this.updating = false;
37353
37354     if (config.el) {
37355         this.el = Roo.get(config.el);
37356         this.initEvents();
37357     }
37358
37359 };
37360
37361 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37362
37363
37364     regions : null,
37365
37366     monitorWindowResize : true,
37367
37368
37369     updating : false,
37370
37371
37372     onRender : function(ct, position)
37373     {
37374         if(!this.el){
37375             this.el = Roo.get(ct);
37376             this.initEvents();
37377         }
37378         //this.fireEvent('render',this);
37379     },
37380
37381
37382     initEvents: function()
37383     {
37384
37385
37386         // ie scrollbar fix
37387         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37388             document.body.scroll = "no";
37389         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37390             this.el.position('relative');
37391         }
37392         this.id = this.el.id;
37393         this.el.addClass("roo-layout-container");
37394         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37395         if(this.el.dom != document.body ) {
37396             this.el.on('resize', this.layout,this);
37397             this.el.on('show', this.layout,this);
37398         }
37399
37400     },
37401
37402     /**
37403      * Returns true if this layout is currently being updated
37404      * @return {Boolean}
37405      */
37406     isUpdating : function(){
37407         return this.updating;
37408     },
37409
37410     /**
37411      * Suspend the LayoutManager from doing auto-layouts while
37412      * making multiple add or remove calls
37413      */
37414     beginUpdate : function(){
37415         this.updating = true;
37416     },
37417
37418     /**
37419      * Restore auto-layouts and optionally disable the manager from performing a layout
37420      * @param {Boolean} noLayout true to disable a layout update
37421      */
37422     endUpdate : function(noLayout){
37423         this.updating = false;
37424         if(!noLayout){
37425             this.layout();
37426         }
37427     },
37428
37429     layout: function(){
37430         // abstract...
37431     },
37432
37433     onRegionResized : function(region, newSize){
37434         this.fireEvent("regionresized", region, newSize);
37435         this.layout();
37436     },
37437
37438     onRegionCollapsed : function(region){
37439         this.fireEvent("regioncollapsed", region);
37440     },
37441
37442     onRegionExpanded : function(region){
37443         this.fireEvent("regionexpanded", region);
37444     },
37445
37446     /**
37447      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37448      * performs box-model adjustments.
37449      * @return {Object} The size as an object {width: (the width), height: (the height)}
37450      */
37451     getViewSize : function()
37452     {
37453         var size;
37454         if(this.el.dom != document.body){
37455             size = this.el.getSize();
37456         }else{
37457             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37458         }
37459         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37460         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37461         return size;
37462     },
37463
37464     /**
37465      * Returns the Element this layout is bound to.
37466      * @return {Roo.Element}
37467      */
37468     getEl : function(){
37469         return this.el;
37470     },
37471
37472     /**
37473      * Returns the specified region.
37474      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37475      * @return {Roo.LayoutRegion}
37476      */
37477     getRegion : function(target){
37478         return this.regions[target.toLowerCase()];
37479     },
37480
37481     onWindowResize : function(){
37482         if(this.monitorWindowResize){
37483             this.layout();
37484         }
37485     }
37486 });
37487 /*
37488  * Based on:
37489  * Ext JS Library 1.1.1
37490  * Copyright(c) 2006-2007, Ext JS, LLC.
37491  *
37492  * Originally Released Under LGPL - original licence link has changed is not relivant.
37493  *
37494  * Fork - LGPL
37495  * <script type="text/javascript">
37496  */
37497 /**
37498  * @class Roo.bootstrap.layout.Border
37499  * @extends Roo.bootstrap.layout.Manager
37500  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37501  * please see: examples/bootstrap/nested.html<br><br>
37502  
37503 <b>The container the layout is rendered into can be either the body element or any other element.
37504 If it is not the body element, the container needs to either be an absolute positioned element,
37505 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37506 the container size if it is not the body element.</b>
37507
37508 * @constructor
37509 * Create a new Border
37510 * @param {Object} config Configuration options
37511  */
37512 Roo.bootstrap.layout.Border = function(config){
37513     config = config || {};
37514     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37515     
37516     
37517     
37518     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37519         if(config[region]){
37520             config[region].region = region;
37521             this.addRegion(config[region]);
37522         }
37523     },this);
37524     
37525 };
37526
37527 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37528
37529 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37530     
37531     parent : false, // this might point to a 'nest' or a ???
37532     
37533     /**
37534      * Creates and adds a new region if it doesn't already exist.
37535      * @param {String} target The target region key (north, south, east, west or center).
37536      * @param {Object} config The regions config object
37537      * @return {BorderLayoutRegion} The new region
37538      */
37539     addRegion : function(config)
37540     {
37541         if(!this.regions[config.region]){
37542             var r = this.factory(config);
37543             this.bindRegion(r);
37544         }
37545         return this.regions[config.region];
37546     },
37547
37548     // private (kinda)
37549     bindRegion : function(r){
37550         this.regions[r.config.region] = r;
37551         
37552         r.on("visibilitychange",    this.layout, this);
37553         r.on("paneladded",          this.layout, this);
37554         r.on("panelremoved",        this.layout, this);
37555         r.on("invalidated",         this.layout, this);
37556         r.on("resized",             this.onRegionResized, this);
37557         r.on("collapsed",           this.onRegionCollapsed, this);
37558         r.on("expanded",            this.onRegionExpanded, this);
37559     },
37560
37561     /**
37562      * Performs a layout update.
37563      */
37564     layout : function()
37565     {
37566         if(this.updating) {
37567             return;
37568         }
37569         
37570         // render all the rebions if they have not been done alreayd?
37571         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37572             if(this.regions[region] && !this.regions[region].bodyEl){
37573                 this.regions[region].onRender(this.el)
37574             }
37575         },this);
37576         
37577         var size = this.getViewSize();
37578         var w = size.width;
37579         var h = size.height;
37580         var centerW = w;
37581         var centerH = h;
37582         var centerY = 0;
37583         var centerX = 0;
37584         //var x = 0, y = 0;
37585
37586         var rs = this.regions;
37587         var north = rs["north"];
37588         var south = rs["south"]; 
37589         var west = rs["west"];
37590         var east = rs["east"];
37591         var center = rs["center"];
37592         //if(this.hideOnLayout){ // not supported anymore
37593             //c.el.setStyle("display", "none");
37594         //}
37595         if(north && north.isVisible()){
37596             var b = north.getBox();
37597             var m = north.getMargins();
37598             b.width = w - (m.left+m.right);
37599             b.x = m.left;
37600             b.y = m.top;
37601             centerY = b.height + b.y + m.bottom;
37602             centerH -= centerY;
37603             north.updateBox(this.safeBox(b));
37604         }
37605         if(south && south.isVisible()){
37606             var b = south.getBox();
37607             var m = south.getMargins();
37608             b.width = w - (m.left+m.right);
37609             b.x = m.left;
37610             var totalHeight = (b.height + m.top + m.bottom);
37611             b.y = h - totalHeight + m.top;
37612             centerH -= totalHeight;
37613             south.updateBox(this.safeBox(b));
37614         }
37615         if(west && west.isVisible()){
37616             var b = west.getBox();
37617             var m = west.getMargins();
37618             b.height = centerH - (m.top+m.bottom);
37619             b.x = m.left;
37620             b.y = centerY + m.top;
37621             var totalWidth = (b.width + m.left + m.right);
37622             centerX += totalWidth;
37623             centerW -= totalWidth;
37624             west.updateBox(this.safeBox(b));
37625         }
37626         if(east && east.isVisible()){
37627             var b = east.getBox();
37628             var m = east.getMargins();
37629             b.height = centerH - (m.top+m.bottom);
37630             var totalWidth = (b.width + m.left + m.right);
37631             b.x = w - totalWidth + m.left;
37632             b.y = centerY + m.top;
37633             centerW -= totalWidth;
37634             east.updateBox(this.safeBox(b));
37635         }
37636         if(center){
37637             var m = center.getMargins();
37638             var centerBox = {
37639                 x: centerX + m.left,
37640                 y: centerY + m.top,
37641                 width: centerW - (m.left+m.right),
37642                 height: centerH - (m.top+m.bottom)
37643             };
37644             //if(this.hideOnLayout){
37645                 //center.el.setStyle("display", "block");
37646             //}
37647             center.updateBox(this.safeBox(centerBox));
37648         }
37649         this.el.repaint();
37650         this.fireEvent("layout", this);
37651     },
37652
37653     // private
37654     safeBox : function(box){
37655         box.width = Math.max(0, box.width);
37656         box.height = Math.max(0, box.height);
37657         return box;
37658     },
37659
37660     /**
37661      * Adds a ContentPanel (or subclass) to this layout.
37662      * @param {String} target The target region key (north, south, east, west or center).
37663      * @param {Roo.ContentPanel} panel The panel to add
37664      * @return {Roo.ContentPanel} The added panel
37665      */
37666     add : function(target, panel){
37667          
37668         target = target.toLowerCase();
37669         return this.regions[target].add(panel);
37670     },
37671
37672     /**
37673      * Remove a ContentPanel (or subclass) to this layout.
37674      * @param {String} target The target region key (north, south, east, west or center).
37675      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37676      * @return {Roo.ContentPanel} The removed panel
37677      */
37678     remove : function(target, panel){
37679         target = target.toLowerCase();
37680         return this.regions[target].remove(panel);
37681     },
37682
37683     /**
37684      * Searches all regions for a panel with the specified id
37685      * @param {String} panelId
37686      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37687      */
37688     findPanel : function(panelId){
37689         var rs = this.regions;
37690         for(var target in rs){
37691             if(typeof rs[target] != "function"){
37692                 var p = rs[target].getPanel(panelId);
37693                 if(p){
37694                     return p;
37695                 }
37696             }
37697         }
37698         return null;
37699     },
37700
37701     /**
37702      * Searches all regions for a panel with the specified id and activates (shows) it.
37703      * @param {String/ContentPanel} panelId The panels id or the panel itself
37704      * @return {Roo.ContentPanel} The shown panel or null
37705      */
37706     showPanel : function(panelId) {
37707       var rs = this.regions;
37708       for(var target in rs){
37709          var r = rs[target];
37710          if(typeof r != "function"){
37711             if(r.hasPanel(panelId)){
37712                return r.showPanel(panelId);
37713             }
37714          }
37715       }
37716       return null;
37717    },
37718
37719    /**
37720      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37721      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37722      */
37723    /*
37724     restoreState : function(provider){
37725         if(!provider){
37726             provider = Roo.state.Manager;
37727         }
37728         var sm = new Roo.LayoutStateManager();
37729         sm.init(this, provider);
37730     },
37731 */
37732  
37733  
37734     /**
37735      * Adds a xtype elements to the layout.
37736      * <pre><code>
37737
37738 layout.addxtype({
37739        xtype : 'ContentPanel',
37740        region: 'west',
37741        items: [ .... ]
37742    }
37743 );
37744
37745 layout.addxtype({
37746         xtype : 'NestedLayoutPanel',
37747         region: 'west',
37748         layout: {
37749            center: { },
37750            west: { }   
37751         },
37752         items : [ ... list of content panels or nested layout panels.. ]
37753    }
37754 );
37755 </code></pre>
37756      * @param {Object} cfg Xtype definition of item to add.
37757      */
37758     addxtype : function(cfg)
37759     {
37760         // basically accepts a pannel...
37761         // can accept a layout region..!?!?
37762         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37763         
37764         
37765         // theory?  children can only be panels??
37766         
37767         //if (!cfg.xtype.match(/Panel$/)) {
37768         //    return false;
37769         //}
37770         var ret = false;
37771         
37772         if (typeof(cfg.region) == 'undefined') {
37773             Roo.log("Failed to add Panel, region was not set");
37774             Roo.log(cfg);
37775             return false;
37776         }
37777         var region = cfg.region;
37778         delete cfg.region;
37779         
37780           
37781         var xitems = [];
37782         if (cfg.items) {
37783             xitems = cfg.items;
37784             delete cfg.items;
37785         }
37786         var nb = false;
37787         
37788         if ( region == 'center') {
37789             Roo.log("Center: " + cfg.title);
37790         }
37791         
37792         
37793         switch(cfg.xtype) 
37794         {
37795             case 'Content':  // ContentPanel (el, cfg)
37796             case 'Scroll':  // ContentPanel (el, cfg)
37797             case 'View': 
37798                 cfg.autoCreate = cfg.autoCreate || true;
37799                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37800                 //} else {
37801                 //    var el = this.el.createChild();
37802                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37803                 //}
37804                 
37805                 this.add(region, ret);
37806                 break;
37807             
37808             /*
37809             case 'TreePanel': // our new panel!
37810                 cfg.el = this.el.createChild();
37811                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37812                 this.add(region, ret);
37813                 break;
37814             */
37815             
37816             case 'Nest': 
37817                 // create a new Layout (which is  a Border Layout...
37818                 
37819                 var clayout = cfg.layout;
37820                 clayout.el  = this.el.createChild();
37821                 clayout.items   = clayout.items  || [];
37822                 
37823                 delete cfg.layout;
37824                 
37825                 // replace this exitems with the clayout ones..
37826                 xitems = clayout.items;
37827                  
37828                 // force background off if it's in center...
37829                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37830                     cfg.background = false;
37831                 }
37832                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37833                 
37834                 
37835                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37836                 //console.log('adding nested layout panel '  + cfg.toSource());
37837                 this.add(region, ret);
37838                 nb = {}; /// find first...
37839                 break;
37840             
37841             case 'Grid':
37842                 
37843                 // needs grid and region
37844                 
37845                 //var el = this.getRegion(region).el.createChild();
37846                 /*
37847                  *var el = this.el.createChild();
37848                 // create the grid first...
37849                 cfg.grid.container = el;
37850                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37851                 */
37852                 
37853                 if (region == 'center' && this.active ) {
37854                     cfg.background = false;
37855                 }
37856                 
37857                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37858                 
37859                 this.add(region, ret);
37860                 /*
37861                 if (cfg.background) {
37862                     // render grid on panel activation (if panel background)
37863                     ret.on('activate', function(gp) {
37864                         if (!gp.grid.rendered) {
37865                     //        gp.grid.render(el);
37866                         }
37867                     });
37868                 } else {
37869                   //  cfg.grid.render(el);
37870                 }
37871                 */
37872                 break;
37873            
37874            
37875             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37876                 // it was the old xcomponent building that caused this before.
37877                 // espeically if border is the top element in the tree.
37878                 ret = this;
37879                 break; 
37880                 
37881                     
37882                 
37883                 
37884                 
37885             default:
37886                 /*
37887                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37888                     
37889                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37890                     this.add(region, ret);
37891                 } else {
37892                 */
37893                     Roo.log(cfg);
37894                     throw "Can not add '" + cfg.xtype + "' to Border";
37895                     return null;
37896              
37897                                 
37898              
37899         }
37900         this.beginUpdate();
37901         // add children..
37902         var region = '';
37903         var abn = {};
37904         Roo.each(xitems, function(i)  {
37905             region = nb && i.region ? i.region : false;
37906             
37907             var add = ret.addxtype(i);
37908            
37909             if (region) {
37910                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37911                 if (!i.background) {
37912                     abn[region] = nb[region] ;
37913                 }
37914             }
37915             
37916         });
37917         this.endUpdate();
37918
37919         // make the last non-background panel active..
37920         //if (nb) { Roo.log(abn); }
37921         if (nb) {
37922             
37923             for(var r in abn) {
37924                 region = this.getRegion(r);
37925                 if (region) {
37926                     // tried using nb[r], but it does not work..
37927                      
37928                     region.showPanel(abn[r]);
37929                    
37930                 }
37931             }
37932         }
37933         return ret;
37934         
37935     },
37936     
37937     
37938 // private
37939     factory : function(cfg)
37940     {
37941         
37942         var validRegions = Roo.bootstrap.layout.Border.regions;
37943
37944         var target = cfg.region;
37945         cfg.mgr = this;
37946         
37947         var r = Roo.bootstrap.layout;
37948         Roo.log(target);
37949         switch(target){
37950             case "north":
37951                 return new r.North(cfg);
37952             case "south":
37953                 return new r.South(cfg);
37954             case "east":
37955                 return new r.East(cfg);
37956             case "west":
37957                 return new r.West(cfg);
37958             case "center":
37959                 return new r.Center(cfg);
37960         }
37961         throw 'Layout region "'+target+'" not supported.';
37962     }
37963     
37964     
37965 });
37966  /*
37967  * Based on:
37968  * Ext JS Library 1.1.1
37969  * Copyright(c) 2006-2007, Ext JS, LLC.
37970  *
37971  * Originally Released Under LGPL - original licence link has changed is not relivant.
37972  *
37973  * Fork - LGPL
37974  * <script type="text/javascript">
37975  */
37976  
37977 /**
37978  * @class Roo.bootstrap.layout.Basic
37979  * @extends Roo.util.Observable
37980  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37981  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37982  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37983  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37984  * @cfg {string}   region  the region that it inhabits..
37985  * @cfg {bool}   skipConfig skip config?
37986  * 
37987
37988  */
37989 Roo.bootstrap.layout.Basic = function(config){
37990     
37991     this.mgr = config.mgr;
37992     
37993     this.position = config.region;
37994     
37995     var skipConfig = config.skipConfig;
37996     
37997     this.events = {
37998         /**
37999          * @scope Roo.BasicLayoutRegion
38000          */
38001         
38002         /**
38003          * @event beforeremove
38004          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38005          * @param {Roo.LayoutRegion} this
38006          * @param {Roo.ContentPanel} panel The panel
38007          * @param {Object} e The cancel event object
38008          */
38009         "beforeremove" : true,
38010         /**
38011          * @event invalidated
38012          * Fires when the layout for this region is changed.
38013          * @param {Roo.LayoutRegion} this
38014          */
38015         "invalidated" : true,
38016         /**
38017          * @event visibilitychange
38018          * Fires when this region is shown or hidden 
38019          * @param {Roo.LayoutRegion} this
38020          * @param {Boolean} visibility true or false
38021          */
38022         "visibilitychange" : true,
38023         /**
38024          * @event paneladded
38025          * Fires when a panel is added. 
38026          * @param {Roo.LayoutRegion} this
38027          * @param {Roo.ContentPanel} panel The panel
38028          */
38029         "paneladded" : true,
38030         /**
38031          * @event panelremoved
38032          * Fires when a panel is removed. 
38033          * @param {Roo.LayoutRegion} this
38034          * @param {Roo.ContentPanel} panel The panel
38035          */
38036         "panelremoved" : true,
38037         /**
38038          * @event beforecollapse
38039          * Fires when this region before collapse.
38040          * @param {Roo.LayoutRegion} this
38041          */
38042         "beforecollapse" : true,
38043         /**
38044          * @event collapsed
38045          * Fires when this region is collapsed.
38046          * @param {Roo.LayoutRegion} this
38047          */
38048         "collapsed" : true,
38049         /**
38050          * @event expanded
38051          * Fires when this region is expanded.
38052          * @param {Roo.LayoutRegion} this
38053          */
38054         "expanded" : true,
38055         /**
38056          * @event slideshow
38057          * Fires when this region is slid into view.
38058          * @param {Roo.LayoutRegion} this
38059          */
38060         "slideshow" : true,
38061         /**
38062          * @event slidehide
38063          * Fires when this region slides out of view. 
38064          * @param {Roo.LayoutRegion} this
38065          */
38066         "slidehide" : true,
38067         /**
38068          * @event panelactivated
38069          * Fires when a panel is activated. 
38070          * @param {Roo.LayoutRegion} this
38071          * @param {Roo.ContentPanel} panel The activated panel
38072          */
38073         "panelactivated" : true,
38074         /**
38075          * @event resized
38076          * Fires when the user resizes this region. 
38077          * @param {Roo.LayoutRegion} this
38078          * @param {Number} newSize The new size (width for east/west, height for north/south)
38079          */
38080         "resized" : true
38081     };
38082     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38083     this.panels = new Roo.util.MixedCollection();
38084     this.panels.getKey = this.getPanelId.createDelegate(this);
38085     this.box = null;
38086     this.activePanel = null;
38087     // ensure listeners are added...
38088     
38089     if (config.listeners || config.events) {
38090         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38091             listeners : config.listeners || {},
38092             events : config.events || {}
38093         });
38094     }
38095     
38096     if(skipConfig !== true){
38097         this.applyConfig(config);
38098     }
38099 };
38100
38101 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38102 {
38103     getPanelId : function(p){
38104         return p.getId();
38105     },
38106     
38107     applyConfig : function(config){
38108         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38109         this.config = config;
38110         
38111     },
38112     
38113     /**
38114      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38115      * the width, for horizontal (north, south) the height.
38116      * @param {Number} newSize The new width or height
38117      */
38118     resizeTo : function(newSize){
38119         var el = this.el ? this.el :
38120                  (this.activePanel ? this.activePanel.getEl() : null);
38121         if(el){
38122             switch(this.position){
38123                 case "east":
38124                 case "west":
38125                     el.setWidth(newSize);
38126                     this.fireEvent("resized", this, newSize);
38127                 break;
38128                 case "north":
38129                 case "south":
38130                     el.setHeight(newSize);
38131                     this.fireEvent("resized", this, newSize);
38132                 break;                
38133             }
38134         }
38135     },
38136     
38137     getBox : function(){
38138         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38139     },
38140     
38141     getMargins : function(){
38142         return this.margins;
38143     },
38144     
38145     updateBox : function(box){
38146         this.box = box;
38147         var el = this.activePanel.getEl();
38148         el.dom.style.left = box.x + "px";
38149         el.dom.style.top = box.y + "px";
38150         this.activePanel.setSize(box.width, box.height);
38151     },
38152     
38153     /**
38154      * Returns the container element for this region.
38155      * @return {Roo.Element}
38156      */
38157     getEl : function(){
38158         return this.activePanel;
38159     },
38160     
38161     /**
38162      * Returns true if this region is currently visible.
38163      * @return {Boolean}
38164      */
38165     isVisible : function(){
38166         return this.activePanel ? true : false;
38167     },
38168     
38169     setActivePanel : function(panel){
38170         panel = this.getPanel(panel);
38171         if(this.activePanel && this.activePanel != panel){
38172             this.activePanel.setActiveState(false);
38173             this.activePanel.getEl().setLeftTop(-10000,-10000);
38174         }
38175         this.activePanel = panel;
38176         panel.setActiveState(true);
38177         if(this.box){
38178             panel.setSize(this.box.width, this.box.height);
38179         }
38180         this.fireEvent("panelactivated", this, panel);
38181         this.fireEvent("invalidated");
38182     },
38183     
38184     /**
38185      * Show the specified panel.
38186      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38187      * @return {Roo.ContentPanel} The shown panel or null
38188      */
38189     showPanel : function(panel){
38190         panel = this.getPanel(panel);
38191         if(panel){
38192             this.setActivePanel(panel);
38193         }
38194         return panel;
38195     },
38196     
38197     /**
38198      * Get the active panel for this region.
38199      * @return {Roo.ContentPanel} The active panel or null
38200      */
38201     getActivePanel : function(){
38202         return this.activePanel;
38203     },
38204     
38205     /**
38206      * Add the passed ContentPanel(s)
38207      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38208      * @return {Roo.ContentPanel} The panel added (if only one was added)
38209      */
38210     add : function(panel){
38211         if(arguments.length > 1){
38212             for(var i = 0, len = arguments.length; i < len; i++) {
38213                 this.add(arguments[i]);
38214             }
38215             return null;
38216         }
38217         if(this.hasPanel(panel)){
38218             this.showPanel(panel);
38219             return panel;
38220         }
38221         var el = panel.getEl();
38222         if(el.dom.parentNode != this.mgr.el.dom){
38223             this.mgr.el.dom.appendChild(el.dom);
38224         }
38225         if(panel.setRegion){
38226             panel.setRegion(this);
38227         }
38228         this.panels.add(panel);
38229         el.setStyle("position", "absolute");
38230         if(!panel.background){
38231             this.setActivePanel(panel);
38232             if(this.config.initialSize && this.panels.getCount()==1){
38233                 this.resizeTo(this.config.initialSize);
38234             }
38235         }
38236         this.fireEvent("paneladded", this, panel);
38237         return panel;
38238     },
38239     
38240     /**
38241      * Returns true if the panel is in this region.
38242      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38243      * @return {Boolean}
38244      */
38245     hasPanel : function(panel){
38246         if(typeof panel == "object"){ // must be panel obj
38247             panel = panel.getId();
38248         }
38249         return this.getPanel(panel) ? true : false;
38250     },
38251     
38252     /**
38253      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38254      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38255      * @param {Boolean} preservePanel Overrides the config preservePanel option
38256      * @return {Roo.ContentPanel} The panel that was removed
38257      */
38258     remove : function(panel, preservePanel){
38259         panel = this.getPanel(panel);
38260         if(!panel){
38261             return null;
38262         }
38263         var e = {};
38264         this.fireEvent("beforeremove", this, panel, e);
38265         if(e.cancel === true){
38266             return null;
38267         }
38268         var panelId = panel.getId();
38269         this.panels.removeKey(panelId);
38270         return panel;
38271     },
38272     
38273     /**
38274      * Returns the panel specified or null if it's not in this region.
38275      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38276      * @return {Roo.ContentPanel}
38277      */
38278     getPanel : function(id){
38279         if(typeof id == "object"){ // must be panel obj
38280             return id;
38281         }
38282         return this.panels.get(id);
38283     },
38284     
38285     /**
38286      * Returns this regions position (north/south/east/west/center).
38287      * @return {String} 
38288      */
38289     getPosition: function(){
38290         return this.position;    
38291     }
38292 });/*
38293  * Based on:
38294  * Ext JS Library 1.1.1
38295  * Copyright(c) 2006-2007, Ext JS, LLC.
38296  *
38297  * Originally Released Under LGPL - original licence link has changed is not relivant.
38298  *
38299  * Fork - LGPL
38300  * <script type="text/javascript">
38301  */
38302  
38303 /**
38304  * @class Roo.bootstrap.layout.Region
38305  * @extends Roo.bootstrap.layout.Basic
38306  * This class represents a region in a layout manager.
38307  
38308  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38309  * @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})
38310  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38311  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38312  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38313  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38314  * @cfg {String}    title           The title for the region (overrides panel titles)
38315  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38316  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38317  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38318  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38319  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38320  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38321  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38322  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38323  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38324  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38325
38326  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38327  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38328  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38329  * @cfg {Number}    width           For East/West panels
38330  * @cfg {Number}    height          For North/South panels
38331  * @cfg {Boolean}   split           To show the splitter
38332  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38333  * 
38334  * @cfg {string}   cls             Extra CSS classes to add to region
38335  * 
38336  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38337  * @cfg {string}   region  the region that it inhabits..
38338  *
38339
38340  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38341  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38342
38343  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38344  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38345  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38346  */
38347 Roo.bootstrap.layout.Region = function(config)
38348 {
38349     this.applyConfig(config);
38350
38351     var mgr = config.mgr;
38352     var pos = config.region;
38353     config.skipConfig = true;
38354     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38355     
38356     if (mgr.el) {
38357         this.onRender(mgr.el);   
38358     }
38359      
38360     this.visible = true;
38361     this.collapsed = false;
38362     this.unrendered_panels = [];
38363 };
38364
38365 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38366
38367     position: '', // set by wrapper (eg. north/south etc..)
38368     unrendered_panels : null,  // unrendered panels.
38369     
38370     tabPosition : false,
38371     
38372     mgr: false, // points to 'Border'
38373     
38374     
38375     createBody : function(){
38376         /** This region's body element 
38377         * @type Roo.Element */
38378         this.bodyEl = this.el.createChild({
38379                 tag: "div",
38380                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38381         });
38382     },
38383
38384     onRender: function(ctr, pos)
38385     {
38386         var dh = Roo.DomHelper;
38387         /** This region's container element 
38388         * @type Roo.Element */
38389         this.el = dh.append(ctr.dom, {
38390                 tag: "div",
38391                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38392             }, true);
38393         /** This region's title element 
38394         * @type Roo.Element */
38395     
38396         this.titleEl = dh.append(this.el.dom,  {
38397                 tag: "div",
38398                 unselectable: "on",
38399                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38400                 children:[
38401                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38402                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38403                 ]
38404             }, true);
38405         
38406         this.titleEl.enableDisplayMode();
38407         /** This region's title text element 
38408         * @type HTMLElement */
38409         this.titleTextEl = this.titleEl.dom.firstChild;
38410         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38411         /*
38412         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38413         this.closeBtn.enableDisplayMode();
38414         this.closeBtn.on("click", this.closeClicked, this);
38415         this.closeBtn.hide();
38416     */
38417         this.createBody(this.config);
38418         if(this.config.hideWhenEmpty){
38419             this.hide();
38420             this.on("paneladded", this.validateVisibility, this);
38421             this.on("panelremoved", this.validateVisibility, this);
38422         }
38423         if(this.autoScroll){
38424             this.bodyEl.setStyle("overflow", "auto");
38425         }else{
38426             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38427         }
38428         //if(c.titlebar !== false){
38429             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38430                 this.titleEl.hide();
38431             }else{
38432                 this.titleEl.show();
38433                 if(this.config.title){
38434                     this.titleTextEl.innerHTML = this.config.title;
38435                 }
38436             }
38437         //}
38438         if(this.config.collapsed){
38439             this.collapse(true);
38440         }
38441         if(this.config.hidden){
38442             this.hide();
38443         }
38444         
38445         if (this.unrendered_panels && this.unrendered_panels.length) {
38446             for (var i =0;i< this.unrendered_panels.length; i++) {
38447                 this.add(this.unrendered_panels[i]);
38448             }
38449             this.unrendered_panels = null;
38450             
38451         }
38452         
38453     },
38454     
38455     applyConfig : function(c)
38456     {
38457         /*
38458          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38459             var dh = Roo.DomHelper;
38460             if(c.titlebar !== false){
38461                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38462                 this.collapseBtn.on("click", this.collapse, this);
38463                 this.collapseBtn.enableDisplayMode();
38464                 /*
38465                 if(c.showPin === true || this.showPin){
38466                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38467                     this.stickBtn.enableDisplayMode();
38468                     this.stickBtn.on("click", this.expand, this);
38469                     this.stickBtn.hide();
38470                 }
38471                 
38472             }
38473             */
38474             /** This region's collapsed element
38475             * @type Roo.Element */
38476             /*
38477              *
38478             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38479                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38480             ]}, true);
38481             
38482             if(c.floatable !== false){
38483                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38484                this.collapsedEl.on("click", this.collapseClick, this);
38485             }
38486
38487             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38488                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38489                    id: "message", unselectable: "on", style:{"float":"left"}});
38490                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38491              }
38492             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38493             this.expandBtn.on("click", this.expand, this);
38494             
38495         }
38496         
38497         if(this.collapseBtn){
38498             this.collapseBtn.setVisible(c.collapsible == true);
38499         }
38500         
38501         this.cmargins = c.cmargins || this.cmargins ||
38502                          (this.position == "west" || this.position == "east" ?
38503                              {top: 0, left: 2, right:2, bottom: 0} :
38504                              {top: 2, left: 0, right:0, bottom: 2});
38505         */
38506         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38507         
38508         
38509         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38510         
38511         this.autoScroll = c.autoScroll || false;
38512         
38513         
38514        
38515         
38516         this.duration = c.duration || .30;
38517         this.slideDuration = c.slideDuration || .45;
38518         this.config = c;
38519        
38520     },
38521     /**
38522      * Returns true if this region is currently visible.
38523      * @return {Boolean}
38524      */
38525     isVisible : function(){
38526         return this.visible;
38527     },
38528
38529     /**
38530      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38531      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38532      */
38533     //setCollapsedTitle : function(title){
38534     //    title = title || "&#160;";
38535      //   if(this.collapsedTitleTextEl){
38536       //      this.collapsedTitleTextEl.innerHTML = title;
38537        // }
38538     //},
38539
38540     getBox : function(){
38541         var b;
38542       //  if(!this.collapsed){
38543             b = this.el.getBox(false, true);
38544        // }else{
38545           //  b = this.collapsedEl.getBox(false, true);
38546         //}
38547         return b;
38548     },
38549
38550     getMargins : function(){
38551         return this.margins;
38552         //return this.collapsed ? this.cmargins : this.margins;
38553     },
38554 /*
38555     highlight : function(){
38556         this.el.addClass("x-layout-panel-dragover");
38557     },
38558
38559     unhighlight : function(){
38560         this.el.removeClass("x-layout-panel-dragover");
38561     },
38562 */
38563     updateBox : function(box)
38564     {
38565         if (!this.bodyEl) {
38566             return; // not rendered yet..
38567         }
38568         
38569         this.box = box;
38570         if(!this.collapsed){
38571             this.el.dom.style.left = box.x + "px";
38572             this.el.dom.style.top = box.y + "px";
38573             this.updateBody(box.width, box.height);
38574         }else{
38575             this.collapsedEl.dom.style.left = box.x + "px";
38576             this.collapsedEl.dom.style.top = box.y + "px";
38577             this.collapsedEl.setSize(box.width, box.height);
38578         }
38579         if(this.tabs){
38580             this.tabs.autoSizeTabs();
38581         }
38582     },
38583
38584     updateBody : function(w, h)
38585     {
38586         if(w !== null){
38587             this.el.setWidth(w);
38588             w -= this.el.getBorderWidth("rl");
38589             if(this.config.adjustments){
38590                 w += this.config.adjustments[0];
38591             }
38592         }
38593         if(h !== null && h > 0){
38594             this.el.setHeight(h);
38595             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38596             h -= this.el.getBorderWidth("tb");
38597             if(this.config.adjustments){
38598                 h += this.config.adjustments[1];
38599             }
38600             this.bodyEl.setHeight(h);
38601             if(this.tabs){
38602                 h = this.tabs.syncHeight(h);
38603             }
38604         }
38605         if(this.panelSize){
38606             w = w !== null ? w : this.panelSize.width;
38607             h = h !== null ? h : this.panelSize.height;
38608         }
38609         if(this.activePanel){
38610             var el = this.activePanel.getEl();
38611             w = w !== null ? w : el.getWidth();
38612             h = h !== null ? h : el.getHeight();
38613             this.panelSize = {width: w, height: h};
38614             this.activePanel.setSize(w, h);
38615         }
38616         if(Roo.isIE && this.tabs){
38617             this.tabs.el.repaint();
38618         }
38619     },
38620
38621     /**
38622      * Returns the container element for this region.
38623      * @return {Roo.Element}
38624      */
38625     getEl : function(){
38626         return this.el;
38627     },
38628
38629     /**
38630      * Hides this region.
38631      */
38632     hide : function(){
38633         //if(!this.collapsed){
38634             this.el.dom.style.left = "-2000px";
38635             this.el.hide();
38636         //}else{
38637          //   this.collapsedEl.dom.style.left = "-2000px";
38638          //   this.collapsedEl.hide();
38639        // }
38640         this.visible = false;
38641         this.fireEvent("visibilitychange", this, false);
38642     },
38643
38644     /**
38645      * Shows this region if it was previously hidden.
38646      */
38647     show : function(){
38648         //if(!this.collapsed){
38649             this.el.show();
38650         //}else{
38651         //    this.collapsedEl.show();
38652        // }
38653         this.visible = true;
38654         this.fireEvent("visibilitychange", this, true);
38655     },
38656 /*
38657     closeClicked : function(){
38658         if(this.activePanel){
38659             this.remove(this.activePanel);
38660         }
38661     },
38662
38663     collapseClick : function(e){
38664         if(this.isSlid){
38665            e.stopPropagation();
38666            this.slideIn();
38667         }else{
38668            e.stopPropagation();
38669            this.slideOut();
38670         }
38671     },
38672 */
38673     /**
38674      * Collapses this region.
38675      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38676      */
38677     /*
38678     collapse : function(skipAnim, skipCheck = false){
38679         if(this.collapsed) {
38680             return;
38681         }
38682         
38683         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38684             
38685             this.collapsed = true;
38686             if(this.split){
38687                 this.split.el.hide();
38688             }
38689             if(this.config.animate && skipAnim !== true){
38690                 this.fireEvent("invalidated", this);
38691                 this.animateCollapse();
38692             }else{
38693                 this.el.setLocation(-20000,-20000);
38694                 this.el.hide();
38695                 this.collapsedEl.show();
38696                 this.fireEvent("collapsed", this);
38697                 this.fireEvent("invalidated", this);
38698             }
38699         }
38700         
38701     },
38702 */
38703     animateCollapse : function(){
38704         // overridden
38705     },
38706
38707     /**
38708      * Expands this region if it was previously collapsed.
38709      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38710      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38711      */
38712     /*
38713     expand : function(e, skipAnim){
38714         if(e) {
38715             e.stopPropagation();
38716         }
38717         if(!this.collapsed || this.el.hasActiveFx()) {
38718             return;
38719         }
38720         if(this.isSlid){
38721             this.afterSlideIn();
38722             skipAnim = true;
38723         }
38724         this.collapsed = false;
38725         if(this.config.animate && skipAnim !== true){
38726             this.animateExpand();
38727         }else{
38728             this.el.show();
38729             if(this.split){
38730                 this.split.el.show();
38731             }
38732             this.collapsedEl.setLocation(-2000,-2000);
38733             this.collapsedEl.hide();
38734             this.fireEvent("invalidated", this);
38735             this.fireEvent("expanded", this);
38736         }
38737     },
38738 */
38739     animateExpand : function(){
38740         // overridden
38741     },
38742
38743     initTabs : function()
38744     {
38745         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38746         
38747         var ts = new Roo.bootstrap.panel.Tabs({
38748             el: this.bodyEl.dom,
38749             region : this,
38750             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38751             disableTooltips: this.config.disableTabTips,
38752             toolbar : this.config.toolbar
38753         });
38754         
38755         if(this.config.hideTabs){
38756             ts.stripWrap.setDisplayed(false);
38757         }
38758         this.tabs = ts;
38759         ts.resizeTabs = this.config.resizeTabs === true;
38760         ts.minTabWidth = this.config.minTabWidth || 40;
38761         ts.maxTabWidth = this.config.maxTabWidth || 250;
38762         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38763         ts.monitorResize = false;
38764         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38765         ts.bodyEl.addClass('roo-layout-tabs-body');
38766         this.panels.each(this.initPanelAsTab, this);
38767     },
38768
38769     initPanelAsTab : function(panel){
38770         var ti = this.tabs.addTab(
38771             panel.getEl().id,
38772             panel.getTitle(),
38773             null,
38774             this.config.closeOnTab && panel.isClosable(),
38775             panel.tpl
38776         );
38777         if(panel.tabTip !== undefined){
38778             ti.setTooltip(panel.tabTip);
38779         }
38780         ti.on("activate", function(){
38781               this.setActivePanel(panel);
38782         }, this);
38783         
38784         if(this.config.closeOnTab){
38785             ti.on("beforeclose", function(t, e){
38786                 e.cancel = true;
38787                 this.remove(panel);
38788             }, this);
38789         }
38790         
38791         panel.tabItem = ti;
38792         
38793         return ti;
38794     },
38795
38796     updatePanelTitle : function(panel, title)
38797     {
38798         if(this.activePanel == panel){
38799             this.updateTitle(title);
38800         }
38801         if(this.tabs){
38802             var ti = this.tabs.getTab(panel.getEl().id);
38803             ti.setText(title);
38804             if(panel.tabTip !== undefined){
38805                 ti.setTooltip(panel.tabTip);
38806             }
38807         }
38808     },
38809
38810     updateTitle : function(title){
38811         if(this.titleTextEl && !this.config.title){
38812             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38813         }
38814     },
38815
38816     setActivePanel : function(panel)
38817     {
38818         panel = this.getPanel(panel);
38819         if(this.activePanel && this.activePanel != panel){
38820             if(this.activePanel.setActiveState(false) === false){
38821                 return;
38822             }
38823         }
38824         this.activePanel = panel;
38825         panel.setActiveState(true);
38826         if(this.panelSize){
38827             panel.setSize(this.panelSize.width, this.panelSize.height);
38828         }
38829         if(this.closeBtn){
38830             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38831         }
38832         this.updateTitle(panel.getTitle());
38833         if(this.tabs){
38834             this.fireEvent("invalidated", this);
38835         }
38836         this.fireEvent("panelactivated", this, panel);
38837     },
38838
38839     /**
38840      * Shows the specified panel.
38841      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38842      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38843      */
38844     showPanel : function(panel)
38845     {
38846         panel = this.getPanel(panel);
38847         if(panel){
38848             if(this.tabs){
38849                 var tab = this.tabs.getTab(panel.getEl().id);
38850                 if(tab.isHidden()){
38851                     this.tabs.unhideTab(tab.id);
38852                 }
38853                 tab.activate();
38854             }else{
38855                 this.setActivePanel(panel);
38856             }
38857         }
38858         return panel;
38859     },
38860
38861     /**
38862      * Get the active panel for this region.
38863      * @return {Roo.ContentPanel} The active panel or null
38864      */
38865     getActivePanel : function(){
38866         return this.activePanel;
38867     },
38868
38869     validateVisibility : function(){
38870         if(this.panels.getCount() < 1){
38871             this.updateTitle("&#160;");
38872             this.closeBtn.hide();
38873             this.hide();
38874         }else{
38875             if(!this.isVisible()){
38876                 this.show();
38877             }
38878         }
38879     },
38880
38881     /**
38882      * Adds the passed ContentPanel(s) to this region.
38883      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38884      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38885      */
38886     add : function(panel)
38887     {
38888         if(arguments.length > 1){
38889             for(var i = 0, len = arguments.length; i < len; i++) {
38890                 this.add(arguments[i]);
38891             }
38892             return null;
38893         }
38894         
38895         // if we have not been rendered yet, then we can not really do much of this..
38896         if (!this.bodyEl) {
38897             this.unrendered_panels.push(panel);
38898             return panel;
38899         }
38900         
38901         
38902         
38903         
38904         if(this.hasPanel(panel)){
38905             this.showPanel(panel);
38906             return panel;
38907         }
38908         panel.setRegion(this);
38909         this.panels.add(panel);
38910        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38911             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38912             // and hide them... ???
38913             this.bodyEl.dom.appendChild(panel.getEl().dom);
38914             if(panel.background !== true){
38915                 this.setActivePanel(panel);
38916             }
38917             this.fireEvent("paneladded", this, panel);
38918             return panel;
38919         }
38920         */
38921         if(!this.tabs){
38922             this.initTabs();
38923         }else{
38924             this.initPanelAsTab(panel);
38925         }
38926         
38927         
38928         if(panel.background !== true){
38929             this.tabs.activate(panel.getEl().id);
38930         }
38931         this.fireEvent("paneladded", this, panel);
38932         return panel;
38933     },
38934
38935     /**
38936      * Hides the tab for the specified panel.
38937      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38938      */
38939     hidePanel : function(panel){
38940         if(this.tabs && (panel = this.getPanel(panel))){
38941             this.tabs.hideTab(panel.getEl().id);
38942         }
38943     },
38944
38945     /**
38946      * Unhides the tab for a previously hidden panel.
38947      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38948      */
38949     unhidePanel : function(panel){
38950         if(this.tabs && (panel = this.getPanel(panel))){
38951             this.tabs.unhideTab(panel.getEl().id);
38952         }
38953     },
38954
38955     clearPanels : function(){
38956         while(this.panels.getCount() > 0){
38957              this.remove(this.panels.first());
38958         }
38959     },
38960
38961     /**
38962      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38963      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38964      * @param {Boolean} preservePanel Overrides the config preservePanel option
38965      * @return {Roo.ContentPanel} The panel that was removed
38966      */
38967     remove : function(panel, preservePanel)
38968     {
38969         panel = this.getPanel(panel);
38970         if(!panel){
38971             return null;
38972         }
38973         var e = {};
38974         this.fireEvent("beforeremove", this, panel, e);
38975         if(e.cancel === true){
38976             return null;
38977         }
38978         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38979         var panelId = panel.getId();
38980         this.panels.removeKey(panelId);
38981         if(preservePanel){
38982             document.body.appendChild(panel.getEl().dom);
38983         }
38984         if(this.tabs){
38985             this.tabs.removeTab(panel.getEl().id);
38986         }else if (!preservePanel){
38987             this.bodyEl.dom.removeChild(panel.getEl().dom);
38988         }
38989         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38990             var p = this.panels.first();
38991             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38992             tempEl.appendChild(p.getEl().dom);
38993             this.bodyEl.update("");
38994             this.bodyEl.dom.appendChild(p.getEl().dom);
38995             tempEl = null;
38996             this.updateTitle(p.getTitle());
38997             this.tabs = null;
38998             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38999             this.setActivePanel(p);
39000         }
39001         panel.setRegion(null);
39002         if(this.activePanel == panel){
39003             this.activePanel = null;
39004         }
39005         if(this.config.autoDestroy !== false && preservePanel !== true){
39006             try{panel.destroy();}catch(e){}
39007         }
39008         this.fireEvent("panelremoved", this, panel);
39009         return panel;
39010     },
39011
39012     /**
39013      * Returns the TabPanel component used by this region
39014      * @return {Roo.TabPanel}
39015      */
39016     getTabs : function(){
39017         return this.tabs;
39018     },
39019
39020     createTool : function(parentEl, className){
39021         var btn = Roo.DomHelper.append(parentEl, {
39022             tag: "div",
39023             cls: "x-layout-tools-button",
39024             children: [ {
39025                 tag: "div",
39026                 cls: "roo-layout-tools-button-inner " + className,
39027                 html: "&#160;"
39028             }]
39029         }, true);
39030         btn.addClassOnOver("roo-layout-tools-button-over");
39031         return btn;
39032     }
39033 });/*
39034  * Based on:
39035  * Ext JS Library 1.1.1
39036  * Copyright(c) 2006-2007, Ext JS, LLC.
39037  *
39038  * Originally Released Under LGPL - original licence link has changed is not relivant.
39039  *
39040  * Fork - LGPL
39041  * <script type="text/javascript">
39042  */
39043  
39044
39045
39046 /**
39047  * @class Roo.SplitLayoutRegion
39048  * @extends Roo.LayoutRegion
39049  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39050  */
39051 Roo.bootstrap.layout.Split = function(config){
39052     this.cursor = config.cursor;
39053     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39054 };
39055
39056 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39057 {
39058     splitTip : "Drag to resize.",
39059     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39060     useSplitTips : false,
39061
39062     applyConfig : function(config){
39063         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39064     },
39065     
39066     onRender : function(ctr,pos) {
39067         
39068         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39069         if(!this.config.split){
39070             return;
39071         }
39072         if(!this.split){
39073             
39074             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39075                             tag: "div",
39076                             id: this.el.id + "-split",
39077                             cls: "roo-layout-split roo-layout-split-"+this.position,
39078                             html: "&#160;"
39079             });
39080             /** The SplitBar for this region 
39081             * @type Roo.SplitBar */
39082             // does not exist yet...
39083             Roo.log([this.position, this.orientation]);
39084             
39085             this.split = new Roo.bootstrap.SplitBar({
39086                 dragElement : splitEl,
39087                 resizingElement: this.el,
39088                 orientation : this.orientation
39089             });
39090             
39091             this.split.on("moved", this.onSplitMove, this);
39092             this.split.useShim = this.config.useShim === true;
39093             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39094             if(this.useSplitTips){
39095                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39096             }
39097             //if(config.collapsible){
39098             //    this.split.el.on("dblclick", this.collapse,  this);
39099             //}
39100         }
39101         if(typeof this.config.minSize != "undefined"){
39102             this.split.minSize = this.config.minSize;
39103         }
39104         if(typeof this.config.maxSize != "undefined"){
39105             this.split.maxSize = this.config.maxSize;
39106         }
39107         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39108             this.hideSplitter();
39109         }
39110         
39111     },
39112
39113     getHMaxSize : function(){
39114          var cmax = this.config.maxSize || 10000;
39115          var center = this.mgr.getRegion("center");
39116          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39117     },
39118
39119     getVMaxSize : function(){
39120          var cmax = this.config.maxSize || 10000;
39121          var center = this.mgr.getRegion("center");
39122          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39123     },
39124
39125     onSplitMove : function(split, newSize){
39126         this.fireEvent("resized", this, newSize);
39127     },
39128     
39129     /** 
39130      * Returns the {@link Roo.SplitBar} for this region.
39131      * @return {Roo.SplitBar}
39132      */
39133     getSplitBar : function(){
39134         return this.split;
39135     },
39136     
39137     hide : function(){
39138         this.hideSplitter();
39139         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39140     },
39141
39142     hideSplitter : function(){
39143         if(this.split){
39144             this.split.el.setLocation(-2000,-2000);
39145             this.split.el.hide();
39146         }
39147     },
39148
39149     show : function(){
39150         if(this.split){
39151             this.split.el.show();
39152         }
39153         Roo.bootstrap.layout.Split.superclass.show.call(this);
39154     },
39155     
39156     beforeSlide: function(){
39157         if(Roo.isGecko){// firefox overflow auto bug workaround
39158             this.bodyEl.clip();
39159             if(this.tabs) {
39160                 this.tabs.bodyEl.clip();
39161             }
39162             if(this.activePanel){
39163                 this.activePanel.getEl().clip();
39164                 
39165                 if(this.activePanel.beforeSlide){
39166                     this.activePanel.beforeSlide();
39167                 }
39168             }
39169         }
39170     },
39171     
39172     afterSlide : function(){
39173         if(Roo.isGecko){// firefox overflow auto bug workaround
39174             this.bodyEl.unclip();
39175             if(this.tabs) {
39176                 this.tabs.bodyEl.unclip();
39177             }
39178             if(this.activePanel){
39179                 this.activePanel.getEl().unclip();
39180                 if(this.activePanel.afterSlide){
39181                     this.activePanel.afterSlide();
39182                 }
39183             }
39184         }
39185     },
39186
39187     initAutoHide : function(){
39188         if(this.autoHide !== false){
39189             if(!this.autoHideHd){
39190                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39191                 this.autoHideHd = {
39192                     "mouseout": function(e){
39193                         if(!e.within(this.el, true)){
39194                             st.delay(500);
39195                         }
39196                     },
39197                     "mouseover" : function(e){
39198                         st.cancel();
39199                     },
39200                     scope : this
39201                 };
39202             }
39203             this.el.on(this.autoHideHd);
39204         }
39205     },
39206
39207     clearAutoHide : function(){
39208         if(this.autoHide !== false){
39209             this.el.un("mouseout", this.autoHideHd.mouseout);
39210             this.el.un("mouseover", this.autoHideHd.mouseover);
39211         }
39212     },
39213
39214     clearMonitor : function(){
39215         Roo.get(document).un("click", this.slideInIf, this);
39216     },
39217
39218     // these names are backwards but not changed for compat
39219     slideOut : function(){
39220         if(this.isSlid || this.el.hasActiveFx()){
39221             return;
39222         }
39223         this.isSlid = true;
39224         if(this.collapseBtn){
39225             this.collapseBtn.hide();
39226         }
39227         this.closeBtnState = this.closeBtn.getStyle('display');
39228         this.closeBtn.hide();
39229         if(this.stickBtn){
39230             this.stickBtn.show();
39231         }
39232         this.el.show();
39233         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39234         this.beforeSlide();
39235         this.el.setStyle("z-index", 10001);
39236         this.el.slideIn(this.getSlideAnchor(), {
39237             callback: function(){
39238                 this.afterSlide();
39239                 this.initAutoHide();
39240                 Roo.get(document).on("click", this.slideInIf, this);
39241                 this.fireEvent("slideshow", this);
39242             },
39243             scope: this,
39244             block: true
39245         });
39246     },
39247
39248     afterSlideIn : function(){
39249         this.clearAutoHide();
39250         this.isSlid = false;
39251         this.clearMonitor();
39252         this.el.setStyle("z-index", "");
39253         if(this.collapseBtn){
39254             this.collapseBtn.show();
39255         }
39256         this.closeBtn.setStyle('display', this.closeBtnState);
39257         if(this.stickBtn){
39258             this.stickBtn.hide();
39259         }
39260         this.fireEvent("slidehide", this);
39261     },
39262
39263     slideIn : function(cb){
39264         if(!this.isSlid || this.el.hasActiveFx()){
39265             Roo.callback(cb);
39266             return;
39267         }
39268         this.isSlid = false;
39269         this.beforeSlide();
39270         this.el.slideOut(this.getSlideAnchor(), {
39271             callback: function(){
39272                 this.el.setLeftTop(-10000, -10000);
39273                 this.afterSlide();
39274                 this.afterSlideIn();
39275                 Roo.callback(cb);
39276             },
39277             scope: this,
39278             block: true
39279         });
39280     },
39281     
39282     slideInIf : function(e){
39283         if(!e.within(this.el)){
39284             this.slideIn();
39285         }
39286     },
39287
39288     animateCollapse : function(){
39289         this.beforeSlide();
39290         this.el.setStyle("z-index", 20000);
39291         var anchor = this.getSlideAnchor();
39292         this.el.slideOut(anchor, {
39293             callback : function(){
39294                 this.el.setStyle("z-index", "");
39295                 this.collapsedEl.slideIn(anchor, {duration:.3});
39296                 this.afterSlide();
39297                 this.el.setLocation(-10000,-10000);
39298                 this.el.hide();
39299                 this.fireEvent("collapsed", this);
39300             },
39301             scope: this,
39302             block: true
39303         });
39304     },
39305
39306     animateExpand : function(){
39307         this.beforeSlide();
39308         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39309         this.el.setStyle("z-index", 20000);
39310         this.collapsedEl.hide({
39311             duration:.1
39312         });
39313         this.el.slideIn(this.getSlideAnchor(), {
39314             callback : function(){
39315                 this.el.setStyle("z-index", "");
39316                 this.afterSlide();
39317                 if(this.split){
39318                     this.split.el.show();
39319                 }
39320                 this.fireEvent("invalidated", this);
39321                 this.fireEvent("expanded", this);
39322             },
39323             scope: this,
39324             block: true
39325         });
39326     },
39327
39328     anchors : {
39329         "west" : "left",
39330         "east" : "right",
39331         "north" : "top",
39332         "south" : "bottom"
39333     },
39334
39335     sanchors : {
39336         "west" : "l",
39337         "east" : "r",
39338         "north" : "t",
39339         "south" : "b"
39340     },
39341
39342     canchors : {
39343         "west" : "tl-tr",
39344         "east" : "tr-tl",
39345         "north" : "tl-bl",
39346         "south" : "bl-tl"
39347     },
39348
39349     getAnchor : function(){
39350         return this.anchors[this.position];
39351     },
39352
39353     getCollapseAnchor : function(){
39354         return this.canchors[this.position];
39355     },
39356
39357     getSlideAnchor : function(){
39358         return this.sanchors[this.position];
39359     },
39360
39361     getAlignAdj : function(){
39362         var cm = this.cmargins;
39363         switch(this.position){
39364             case "west":
39365                 return [0, 0];
39366             break;
39367             case "east":
39368                 return [0, 0];
39369             break;
39370             case "north":
39371                 return [0, 0];
39372             break;
39373             case "south":
39374                 return [0, 0];
39375             break;
39376         }
39377     },
39378
39379     getExpandAdj : function(){
39380         var c = this.collapsedEl, cm = this.cmargins;
39381         switch(this.position){
39382             case "west":
39383                 return [-(cm.right+c.getWidth()+cm.left), 0];
39384             break;
39385             case "east":
39386                 return [cm.right+c.getWidth()+cm.left, 0];
39387             break;
39388             case "north":
39389                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39390             break;
39391             case "south":
39392                 return [0, cm.top+cm.bottom+c.getHeight()];
39393             break;
39394         }
39395     }
39396 });/*
39397  * Based on:
39398  * Ext JS Library 1.1.1
39399  * Copyright(c) 2006-2007, Ext JS, LLC.
39400  *
39401  * Originally Released Under LGPL - original licence link has changed is not relivant.
39402  *
39403  * Fork - LGPL
39404  * <script type="text/javascript">
39405  */
39406 /*
39407  * These classes are private internal classes
39408  */
39409 Roo.bootstrap.layout.Center = function(config){
39410     config.region = "center";
39411     Roo.bootstrap.layout.Region.call(this, config);
39412     this.visible = true;
39413     this.minWidth = config.minWidth || 20;
39414     this.minHeight = config.minHeight || 20;
39415 };
39416
39417 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39418     hide : function(){
39419         // center panel can't be hidden
39420     },
39421     
39422     show : function(){
39423         // center panel can't be hidden
39424     },
39425     
39426     getMinWidth: function(){
39427         return this.minWidth;
39428     },
39429     
39430     getMinHeight: function(){
39431         return this.minHeight;
39432     }
39433 });
39434
39435
39436
39437
39438  
39439
39440
39441
39442
39443
39444
39445 Roo.bootstrap.layout.North = function(config)
39446 {
39447     config.region = 'north';
39448     config.cursor = 'n-resize';
39449     
39450     Roo.bootstrap.layout.Split.call(this, config);
39451     
39452     
39453     if(this.split){
39454         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39455         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39456         this.split.el.addClass("roo-layout-split-v");
39457     }
39458     //var size = config.initialSize || config.height;
39459     //if(this.el && typeof size != "undefined"){
39460     //    this.el.setHeight(size);
39461     //}
39462 };
39463 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39464 {
39465     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39466      
39467      
39468     onRender : function(ctr, pos)
39469     {
39470         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39471         var size = this.config.initialSize || this.config.height;
39472         if(this.el && typeof size != "undefined"){
39473             this.el.setHeight(size);
39474         }
39475     
39476     },
39477     
39478     getBox : function(){
39479         if(this.collapsed){
39480             return this.collapsedEl.getBox();
39481         }
39482         var box = this.el.getBox();
39483         if(this.split){
39484             box.height += this.split.el.getHeight();
39485         }
39486         return box;
39487     },
39488     
39489     updateBox : function(box){
39490         if(this.split && !this.collapsed){
39491             box.height -= this.split.el.getHeight();
39492             this.split.el.setLeft(box.x);
39493             this.split.el.setTop(box.y+box.height);
39494             this.split.el.setWidth(box.width);
39495         }
39496         if(this.collapsed){
39497             this.updateBody(box.width, null);
39498         }
39499         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39500     }
39501 });
39502
39503
39504
39505
39506
39507 Roo.bootstrap.layout.South = function(config){
39508     config.region = 'south';
39509     config.cursor = 's-resize';
39510     Roo.bootstrap.layout.Split.call(this, config);
39511     if(this.split){
39512         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39513         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39514         this.split.el.addClass("roo-layout-split-v");
39515     }
39516     
39517 };
39518
39519 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39520     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39521     
39522     onRender : function(ctr, pos)
39523     {
39524         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39525         var size = this.config.initialSize || this.config.height;
39526         if(this.el && typeof size != "undefined"){
39527             this.el.setHeight(size);
39528         }
39529     
39530     },
39531     
39532     getBox : function(){
39533         if(this.collapsed){
39534             return this.collapsedEl.getBox();
39535         }
39536         var box = this.el.getBox();
39537         if(this.split){
39538             var sh = this.split.el.getHeight();
39539             box.height += sh;
39540             box.y -= sh;
39541         }
39542         return box;
39543     },
39544     
39545     updateBox : function(box){
39546         if(this.split && !this.collapsed){
39547             var sh = this.split.el.getHeight();
39548             box.height -= sh;
39549             box.y += sh;
39550             this.split.el.setLeft(box.x);
39551             this.split.el.setTop(box.y-sh);
39552             this.split.el.setWidth(box.width);
39553         }
39554         if(this.collapsed){
39555             this.updateBody(box.width, null);
39556         }
39557         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39558     }
39559 });
39560
39561 Roo.bootstrap.layout.East = function(config){
39562     config.region = "east";
39563     config.cursor = "e-resize";
39564     Roo.bootstrap.layout.Split.call(this, config);
39565     if(this.split){
39566         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39567         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39568         this.split.el.addClass("roo-layout-split-h");
39569     }
39570     
39571 };
39572 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39573     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39574     
39575     onRender : function(ctr, pos)
39576     {
39577         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39578         var size = this.config.initialSize || this.config.width;
39579         if(this.el && typeof size != "undefined"){
39580             this.el.setWidth(size);
39581         }
39582     
39583     },
39584     
39585     getBox : function(){
39586         if(this.collapsed){
39587             return this.collapsedEl.getBox();
39588         }
39589         var box = this.el.getBox();
39590         if(this.split){
39591             var sw = this.split.el.getWidth();
39592             box.width += sw;
39593             box.x -= sw;
39594         }
39595         return box;
39596     },
39597
39598     updateBox : function(box){
39599         if(this.split && !this.collapsed){
39600             var sw = this.split.el.getWidth();
39601             box.width -= sw;
39602             this.split.el.setLeft(box.x);
39603             this.split.el.setTop(box.y);
39604             this.split.el.setHeight(box.height);
39605             box.x += sw;
39606         }
39607         if(this.collapsed){
39608             this.updateBody(null, box.height);
39609         }
39610         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39611     }
39612 });
39613
39614 Roo.bootstrap.layout.West = function(config){
39615     config.region = "west";
39616     config.cursor = "w-resize";
39617     
39618     Roo.bootstrap.layout.Split.call(this, config);
39619     if(this.split){
39620         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39621         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39622         this.split.el.addClass("roo-layout-split-h");
39623     }
39624     
39625 };
39626 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39627     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39628     
39629     onRender: function(ctr, pos)
39630     {
39631         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39632         var size = this.config.initialSize || this.config.width;
39633         if(typeof size != "undefined"){
39634             this.el.setWidth(size);
39635         }
39636     },
39637     
39638     getBox : function(){
39639         if(this.collapsed){
39640             return this.collapsedEl.getBox();
39641         }
39642         var box = this.el.getBox();
39643         if (box.width == 0) {
39644             box.width = this.config.width; // kludge?
39645         }
39646         if(this.split){
39647             box.width += this.split.el.getWidth();
39648         }
39649         return box;
39650     },
39651     
39652     updateBox : function(box){
39653         if(this.split && !this.collapsed){
39654             var sw = this.split.el.getWidth();
39655             box.width -= sw;
39656             this.split.el.setLeft(box.x+box.width);
39657             this.split.el.setTop(box.y);
39658             this.split.el.setHeight(box.height);
39659         }
39660         if(this.collapsed){
39661             this.updateBody(null, box.height);
39662         }
39663         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39664     }
39665 });Roo.namespace("Roo.bootstrap.panel");/*
39666  * Based on:
39667  * Ext JS Library 1.1.1
39668  * Copyright(c) 2006-2007, Ext JS, LLC.
39669  *
39670  * Originally Released Under LGPL - original licence link has changed is not relivant.
39671  *
39672  * Fork - LGPL
39673  * <script type="text/javascript">
39674  */
39675 /**
39676  * @class Roo.ContentPanel
39677  * @extends Roo.util.Observable
39678  * A basic ContentPanel element.
39679  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39680  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39681  * @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
39682  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39683  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39684  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39685  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39686  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39687  * @cfg {String} title          The title for this panel
39688  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39689  * @cfg {String} url            Calls {@link #setUrl} with this value
39690  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39691  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39692  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39693  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39694  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39695  * @cfg {Boolean} badges render the badges
39696  * @cfg {String} cls  extra classes to use  
39697  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39698
39699  * @constructor
39700  * Create a new ContentPanel.
39701  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39702  * @param {String/Object} config A string to set only the title or a config object
39703  * @param {String} content (optional) Set the HTML content for this panel
39704  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39705  */
39706 Roo.bootstrap.panel.Content = function( config){
39707     
39708     this.tpl = config.tpl || false;
39709     
39710     var el = config.el;
39711     var content = config.content;
39712
39713     if(config.autoCreate){ // xtype is available if this is called from factory
39714         el = Roo.id();
39715     }
39716     this.el = Roo.get(el);
39717     if(!this.el && config && config.autoCreate){
39718         if(typeof config.autoCreate == "object"){
39719             if(!config.autoCreate.id){
39720                 config.autoCreate.id = config.id||el;
39721             }
39722             this.el = Roo.DomHelper.append(document.body,
39723                         config.autoCreate, true);
39724         }else{
39725             var elcfg =  {
39726                 tag: "div",
39727                 cls: (config.cls || '') +
39728                     (config.background ? ' bg-' + config.background : '') +
39729                     " roo-layout-inactive-content",
39730                 id: config.id||el
39731             };
39732             if (config.iframe) {
39733                 elcfg.cn = [
39734                     {
39735                         tag : 'iframe',
39736                         style : 'border: 0px',
39737                         src : 'about:blank'
39738                     }
39739                 ];
39740             }
39741               
39742             if (config.html) {
39743                 elcfg.html = config.html;
39744                 
39745             }
39746                         
39747             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39748             if (config.iframe) {
39749                 this.iframeEl = this.el.select('iframe',true).first();
39750             }
39751             
39752         }
39753     } 
39754     this.closable = false;
39755     this.loaded = false;
39756     this.active = false;
39757    
39758       
39759     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39760         
39761         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39762         
39763         this.wrapEl = this.el; //this.el.wrap();
39764         var ti = [];
39765         if (config.toolbar.items) {
39766             ti = config.toolbar.items ;
39767             delete config.toolbar.items ;
39768         }
39769         
39770         var nitems = [];
39771         this.toolbar.render(this.wrapEl, 'before');
39772         for(var i =0;i < ti.length;i++) {
39773           //  Roo.log(['add child', items[i]]);
39774             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39775         }
39776         this.toolbar.items = nitems;
39777         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39778         delete config.toolbar;
39779         
39780     }
39781     /*
39782     // xtype created footer. - not sure if will work as we normally have to render first..
39783     if (this.footer && !this.footer.el && this.footer.xtype) {
39784         if (!this.wrapEl) {
39785             this.wrapEl = this.el.wrap();
39786         }
39787     
39788         this.footer.container = this.wrapEl.createChild();
39789          
39790         this.footer = Roo.factory(this.footer, Roo);
39791         
39792     }
39793     */
39794     
39795      if(typeof config == "string"){
39796         this.title = config;
39797     }else{
39798         Roo.apply(this, config);
39799     }
39800     
39801     if(this.resizeEl){
39802         this.resizeEl = Roo.get(this.resizeEl, true);
39803     }else{
39804         this.resizeEl = this.el;
39805     }
39806     // handle view.xtype
39807     
39808  
39809     
39810     
39811     this.addEvents({
39812         /**
39813          * @event activate
39814          * Fires when this panel is activated. 
39815          * @param {Roo.ContentPanel} this
39816          */
39817         "activate" : true,
39818         /**
39819          * @event deactivate
39820          * Fires when this panel is activated. 
39821          * @param {Roo.ContentPanel} this
39822          */
39823         "deactivate" : true,
39824
39825         /**
39826          * @event resize
39827          * Fires when this panel is resized if fitToFrame is true.
39828          * @param {Roo.ContentPanel} this
39829          * @param {Number} width The width after any component adjustments
39830          * @param {Number} height The height after any component adjustments
39831          */
39832         "resize" : true,
39833         
39834          /**
39835          * @event render
39836          * Fires when this tab is created
39837          * @param {Roo.ContentPanel} this
39838          */
39839         "render" : true
39840         
39841         
39842         
39843     });
39844     
39845
39846     
39847     
39848     if(this.autoScroll && !this.iframe){
39849         this.resizeEl.setStyle("overflow", "auto");
39850     } else {
39851         // fix randome scrolling
39852         //this.el.on('scroll', function() {
39853         //    Roo.log('fix random scolling');
39854         //    this.scrollTo('top',0); 
39855         //});
39856     }
39857     content = content || this.content;
39858     if(content){
39859         this.setContent(content);
39860     }
39861     if(config && config.url){
39862         this.setUrl(this.url, this.params, this.loadOnce);
39863     }
39864     
39865     
39866     
39867     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39868     
39869     if (this.view && typeof(this.view.xtype) != 'undefined') {
39870         this.view.el = this.el.appendChild(document.createElement("div"));
39871         this.view = Roo.factory(this.view); 
39872         this.view.render  &&  this.view.render(false, '');  
39873     }
39874     
39875     
39876     this.fireEvent('render', this);
39877 };
39878
39879 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39880     
39881     cls : '',
39882     background : '',
39883     
39884     tabTip : '',
39885     
39886     iframe : false,
39887     iframeEl : false,
39888     
39889     setRegion : function(region){
39890         this.region = region;
39891         this.setActiveClass(region && !this.background);
39892     },
39893     
39894     
39895     setActiveClass: function(state)
39896     {
39897         if(state){
39898            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39899            this.el.setStyle('position','relative');
39900         }else{
39901            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39902            this.el.setStyle('position', 'absolute');
39903         } 
39904     },
39905     
39906     /**
39907      * Returns the toolbar for this Panel if one was configured. 
39908      * @return {Roo.Toolbar} 
39909      */
39910     getToolbar : function(){
39911         return this.toolbar;
39912     },
39913     
39914     setActiveState : function(active)
39915     {
39916         this.active = active;
39917         this.setActiveClass(active);
39918         if(!active){
39919             if(this.fireEvent("deactivate", this) === false){
39920                 return false;
39921             }
39922             return true;
39923         }
39924         this.fireEvent("activate", this);
39925         return true;
39926     },
39927     /**
39928      * Updates this panel's element (not for iframe)
39929      * @param {String} content The new content
39930      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39931     */
39932     setContent : function(content, loadScripts){
39933         if (this.iframe) {
39934             return;
39935         }
39936         
39937         this.el.update(content, loadScripts);
39938     },
39939
39940     ignoreResize : function(w, h){
39941         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39942             return true;
39943         }else{
39944             this.lastSize = {width: w, height: h};
39945             return false;
39946         }
39947     },
39948     /**
39949      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39950      * @return {Roo.UpdateManager} The UpdateManager
39951      */
39952     getUpdateManager : function(){
39953         if (this.iframe) {
39954             return false;
39955         }
39956         return this.el.getUpdateManager();
39957     },
39958      /**
39959      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39960      * Does not work with IFRAME contents
39961      * @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:
39962 <pre><code>
39963 panel.load({
39964     url: "your-url.php",
39965     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39966     callback: yourFunction,
39967     scope: yourObject, //(optional scope)
39968     discardUrl: false,
39969     nocache: false,
39970     text: "Loading...",
39971     timeout: 30,
39972     scripts: false
39973 });
39974 </code></pre>
39975      
39976      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39977      * 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.
39978      * @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}
39979      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39980      * @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.
39981      * @return {Roo.ContentPanel} this
39982      */
39983     load : function(){
39984         
39985         if (this.iframe) {
39986             return this;
39987         }
39988         
39989         var um = this.el.getUpdateManager();
39990         um.update.apply(um, arguments);
39991         return this;
39992     },
39993
39994
39995     /**
39996      * 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.
39997      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39998      * @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)
39999      * @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)
40000      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40001      */
40002     setUrl : function(url, params, loadOnce){
40003         if (this.iframe) {
40004             this.iframeEl.dom.src = url;
40005             return false;
40006         }
40007         
40008         if(this.refreshDelegate){
40009             this.removeListener("activate", this.refreshDelegate);
40010         }
40011         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40012         this.on("activate", this.refreshDelegate);
40013         return this.el.getUpdateManager();
40014     },
40015     
40016     _handleRefresh : function(url, params, loadOnce){
40017         if(!loadOnce || !this.loaded){
40018             var updater = this.el.getUpdateManager();
40019             updater.update(url, params, this._setLoaded.createDelegate(this));
40020         }
40021     },
40022     
40023     _setLoaded : function(){
40024         this.loaded = true;
40025     }, 
40026     
40027     /**
40028      * Returns this panel's id
40029      * @return {String} 
40030      */
40031     getId : function(){
40032         return this.el.id;
40033     },
40034     
40035     /** 
40036      * Returns this panel's element - used by regiosn to add.
40037      * @return {Roo.Element} 
40038      */
40039     getEl : function(){
40040         return this.wrapEl || this.el;
40041     },
40042     
40043    
40044     
40045     adjustForComponents : function(width, height)
40046     {
40047         //Roo.log('adjustForComponents ');
40048         if(this.resizeEl != this.el){
40049             width -= this.el.getFrameWidth('lr');
40050             height -= this.el.getFrameWidth('tb');
40051         }
40052         if(this.toolbar){
40053             var te = this.toolbar.getEl();
40054             te.setWidth(width);
40055             height -= te.getHeight();
40056         }
40057         if(this.footer){
40058             var te = this.footer.getEl();
40059             te.setWidth(width);
40060             height -= te.getHeight();
40061         }
40062         
40063         
40064         if(this.adjustments){
40065             width += this.adjustments[0];
40066             height += this.adjustments[1];
40067         }
40068         return {"width": width, "height": height};
40069     },
40070     
40071     setSize : function(width, height){
40072         if(this.fitToFrame && !this.ignoreResize(width, height)){
40073             if(this.fitContainer && this.resizeEl != this.el){
40074                 this.el.setSize(width, height);
40075             }
40076             var size = this.adjustForComponents(width, height);
40077             if (this.iframe) {
40078                 this.iframeEl.setSize(width,height);
40079             }
40080             
40081             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40082             this.fireEvent('resize', this, size.width, size.height);
40083             
40084             
40085         }
40086     },
40087     
40088     /**
40089      * Returns this panel's title
40090      * @return {String} 
40091      */
40092     getTitle : function(){
40093         
40094         if (typeof(this.title) != 'object') {
40095             return this.title;
40096         }
40097         
40098         var t = '';
40099         for (var k in this.title) {
40100             if (!this.title.hasOwnProperty(k)) {
40101                 continue;
40102             }
40103             
40104             if (k.indexOf('-') >= 0) {
40105                 var s = k.split('-');
40106                 for (var i = 0; i<s.length; i++) {
40107                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40108                 }
40109             } else {
40110                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40111             }
40112         }
40113         return t;
40114     },
40115     
40116     /**
40117      * Set this panel's title
40118      * @param {String} title
40119      */
40120     setTitle : function(title){
40121         this.title = title;
40122         if(this.region){
40123             this.region.updatePanelTitle(this, title);
40124         }
40125     },
40126     
40127     /**
40128      * Returns true is this panel was configured to be closable
40129      * @return {Boolean} 
40130      */
40131     isClosable : function(){
40132         return this.closable;
40133     },
40134     
40135     beforeSlide : function(){
40136         this.el.clip();
40137         this.resizeEl.clip();
40138     },
40139     
40140     afterSlide : function(){
40141         this.el.unclip();
40142         this.resizeEl.unclip();
40143     },
40144     
40145     /**
40146      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40147      *   Will fail silently if the {@link #setUrl} method has not been called.
40148      *   This does not activate the panel, just updates its content.
40149      */
40150     refresh : function(){
40151         if(this.refreshDelegate){
40152            this.loaded = false;
40153            this.refreshDelegate();
40154         }
40155     },
40156     
40157     /**
40158      * Destroys this panel
40159      */
40160     destroy : function(){
40161         this.el.removeAllListeners();
40162         var tempEl = document.createElement("span");
40163         tempEl.appendChild(this.el.dom);
40164         tempEl.innerHTML = "";
40165         this.el.remove();
40166         this.el = null;
40167     },
40168     
40169     /**
40170      * form - if the content panel contains a form - this is a reference to it.
40171      * @type {Roo.form.Form}
40172      */
40173     form : false,
40174     /**
40175      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40176      *    This contains a reference to it.
40177      * @type {Roo.View}
40178      */
40179     view : false,
40180     
40181       /**
40182      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40183      * <pre><code>
40184
40185 layout.addxtype({
40186        xtype : 'Form',
40187        items: [ .... ]
40188    }
40189 );
40190
40191 </code></pre>
40192      * @param {Object} cfg Xtype definition of item to add.
40193      */
40194     
40195     
40196     getChildContainer: function () {
40197         return this.getEl();
40198     }
40199     
40200     
40201     /*
40202         var  ret = new Roo.factory(cfg);
40203         return ret;
40204         
40205         
40206         // add form..
40207         if (cfg.xtype.match(/^Form$/)) {
40208             
40209             var el;
40210             //if (this.footer) {
40211             //    el = this.footer.container.insertSibling(false, 'before');
40212             //} else {
40213                 el = this.el.createChild();
40214             //}
40215
40216             this.form = new  Roo.form.Form(cfg);
40217             
40218             
40219             if ( this.form.allItems.length) {
40220                 this.form.render(el.dom);
40221             }
40222             return this.form;
40223         }
40224         // should only have one of theses..
40225         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40226             // views.. should not be just added - used named prop 'view''
40227             
40228             cfg.el = this.el.appendChild(document.createElement("div"));
40229             // factory?
40230             
40231             var ret = new Roo.factory(cfg);
40232              
40233              ret.render && ret.render(false, ''); // render blank..
40234             this.view = ret;
40235             return ret;
40236         }
40237         return false;
40238     }
40239     \*/
40240 });
40241  
40242 /**
40243  * @class Roo.bootstrap.panel.Grid
40244  * @extends Roo.bootstrap.panel.Content
40245  * @constructor
40246  * Create a new GridPanel.
40247  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40248  * @param {Object} config A the config object
40249   
40250  */
40251
40252
40253
40254 Roo.bootstrap.panel.Grid = function(config)
40255 {
40256     
40257       
40258     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40259         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40260
40261     config.el = this.wrapper;
40262     //this.el = this.wrapper;
40263     
40264       if (config.container) {
40265         // ctor'ed from a Border/panel.grid
40266         
40267         
40268         this.wrapper.setStyle("overflow", "hidden");
40269         this.wrapper.addClass('roo-grid-container');
40270
40271     }
40272     
40273     
40274     if(config.toolbar){
40275         var tool_el = this.wrapper.createChild();    
40276         this.toolbar = Roo.factory(config.toolbar);
40277         var ti = [];
40278         if (config.toolbar.items) {
40279             ti = config.toolbar.items ;
40280             delete config.toolbar.items ;
40281         }
40282         
40283         var nitems = [];
40284         this.toolbar.render(tool_el);
40285         for(var i =0;i < ti.length;i++) {
40286           //  Roo.log(['add child', items[i]]);
40287             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40288         }
40289         this.toolbar.items = nitems;
40290         
40291         delete config.toolbar;
40292     }
40293     
40294     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40295     config.grid.scrollBody = true;;
40296     config.grid.monitorWindowResize = false; // turn off autosizing
40297     config.grid.autoHeight = false;
40298     config.grid.autoWidth = false;
40299     
40300     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40301     
40302     if (config.background) {
40303         // render grid on panel activation (if panel background)
40304         this.on('activate', function(gp) {
40305             if (!gp.grid.rendered) {
40306                 gp.grid.render(this.wrapper);
40307                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40308             }
40309         });
40310             
40311     } else {
40312         this.grid.render(this.wrapper);
40313         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40314
40315     }
40316     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40317     // ??? needed ??? config.el = this.wrapper;
40318     
40319     
40320     
40321   
40322     // xtype created footer. - not sure if will work as we normally have to render first..
40323     if (this.footer && !this.footer.el && this.footer.xtype) {
40324         
40325         var ctr = this.grid.getView().getFooterPanel(true);
40326         this.footer.dataSource = this.grid.dataSource;
40327         this.footer = Roo.factory(this.footer, Roo);
40328         this.footer.render(ctr);
40329         
40330     }
40331     
40332     
40333     
40334     
40335      
40336 };
40337
40338 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40339     getId : function(){
40340         return this.grid.id;
40341     },
40342     
40343     /**
40344      * Returns the grid for this panel
40345      * @return {Roo.bootstrap.Table} 
40346      */
40347     getGrid : function(){
40348         return this.grid;    
40349     },
40350     
40351     setSize : function(width, height){
40352         if(!this.ignoreResize(width, height)){
40353             var grid = this.grid;
40354             var size = this.adjustForComponents(width, height);
40355             // tfoot is not a footer?
40356           
40357             
40358             var gridel = grid.getGridEl();
40359             gridel.setSize(size.width, size.height);
40360             
40361             var tbd = grid.getGridEl().select('tbody', true).first();
40362             var thd = grid.getGridEl().select('thead',true).first();
40363             var tbf= grid.getGridEl().select('tfoot', true).first();
40364
40365             if (tbf) {
40366                 size.height -= tbf.getHeight();
40367             }
40368             if (thd) {
40369                 size.height -= thd.getHeight();
40370             }
40371             
40372             tbd.setSize(size.width, size.height );
40373             // this is for the account management tab -seems to work there.
40374             var thd = grid.getGridEl().select('thead',true).first();
40375             //if (tbd) {
40376             //    tbd.setSize(size.width, size.height - thd.getHeight());
40377             //}
40378              
40379             grid.autoSize();
40380         }
40381     },
40382      
40383     
40384     
40385     beforeSlide : function(){
40386         this.grid.getView().scroller.clip();
40387     },
40388     
40389     afterSlide : function(){
40390         this.grid.getView().scroller.unclip();
40391     },
40392     
40393     destroy : function(){
40394         this.grid.destroy();
40395         delete this.grid;
40396         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40397     }
40398 });
40399
40400 /**
40401  * @class Roo.bootstrap.panel.Nest
40402  * @extends Roo.bootstrap.panel.Content
40403  * @constructor
40404  * Create a new Panel, that can contain a layout.Border.
40405  * 
40406  * 
40407  * @param {Roo.BorderLayout} layout The layout for this panel
40408  * @param {String/Object} config A string to set only the title or a config object
40409  */
40410 Roo.bootstrap.panel.Nest = function(config)
40411 {
40412     // construct with only one argument..
40413     /* FIXME - implement nicer consturctors
40414     if (layout.layout) {
40415         config = layout;
40416         layout = config.layout;
40417         delete config.layout;
40418     }
40419     if (layout.xtype && !layout.getEl) {
40420         // then layout needs constructing..
40421         layout = Roo.factory(layout, Roo);
40422     }
40423     */
40424     
40425     config.el =  config.layout.getEl();
40426     
40427     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40428     
40429     config.layout.monitorWindowResize = false; // turn off autosizing
40430     this.layout = config.layout;
40431     this.layout.getEl().addClass("roo-layout-nested-layout");
40432     this.layout.parent = this;
40433     
40434     
40435     
40436     
40437 };
40438
40439 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40440
40441     setSize : function(width, height){
40442         if(!this.ignoreResize(width, height)){
40443             var size = this.adjustForComponents(width, height);
40444             var el = this.layout.getEl();
40445             if (size.height < 1) {
40446                 el.setWidth(size.width);   
40447             } else {
40448                 el.setSize(size.width, size.height);
40449             }
40450             var touch = el.dom.offsetWidth;
40451             this.layout.layout();
40452             // ie requires a double layout on the first pass
40453             if(Roo.isIE && !this.initialized){
40454                 this.initialized = true;
40455                 this.layout.layout();
40456             }
40457         }
40458     },
40459     
40460     // activate all subpanels if not currently active..
40461     
40462     setActiveState : function(active){
40463         this.active = active;
40464         this.setActiveClass(active);
40465         
40466         if(!active){
40467             this.fireEvent("deactivate", this);
40468             return;
40469         }
40470         
40471         this.fireEvent("activate", this);
40472         // not sure if this should happen before or after..
40473         if (!this.layout) {
40474             return; // should not happen..
40475         }
40476         var reg = false;
40477         for (var r in this.layout.regions) {
40478             reg = this.layout.getRegion(r);
40479             if (reg.getActivePanel()) {
40480                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40481                 reg.setActivePanel(reg.getActivePanel());
40482                 continue;
40483             }
40484             if (!reg.panels.length) {
40485                 continue;
40486             }
40487             reg.showPanel(reg.getPanel(0));
40488         }
40489         
40490         
40491         
40492         
40493     },
40494     
40495     /**
40496      * Returns the nested BorderLayout for this panel
40497      * @return {Roo.BorderLayout} 
40498      */
40499     getLayout : function(){
40500         return this.layout;
40501     },
40502     
40503      /**
40504      * Adds a xtype elements to the layout of the nested panel
40505      * <pre><code>
40506
40507 panel.addxtype({
40508        xtype : 'ContentPanel',
40509        region: 'west',
40510        items: [ .... ]
40511    }
40512 );
40513
40514 panel.addxtype({
40515         xtype : 'NestedLayoutPanel',
40516         region: 'west',
40517         layout: {
40518            center: { },
40519            west: { }   
40520         },
40521         items : [ ... list of content panels or nested layout panels.. ]
40522    }
40523 );
40524 </code></pre>
40525      * @param {Object} cfg Xtype definition of item to add.
40526      */
40527     addxtype : function(cfg) {
40528         return this.layout.addxtype(cfg);
40529     
40530     }
40531 });/*
40532  * Based on:
40533  * Ext JS Library 1.1.1
40534  * Copyright(c) 2006-2007, Ext JS, LLC.
40535  *
40536  * Originally Released Under LGPL - original licence link has changed is not relivant.
40537  *
40538  * Fork - LGPL
40539  * <script type="text/javascript">
40540  */
40541 /**
40542  * @class Roo.TabPanel
40543  * @extends Roo.util.Observable
40544  * A lightweight tab container.
40545  * <br><br>
40546  * Usage:
40547  * <pre><code>
40548 // basic tabs 1, built from existing content
40549 var tabs = new Roo.TabPanel("tabs1");
40550 tabs.addTab("script", "View Script");
40551 tabs.addTab("markup", "View Markup");
40552 tabs.activate("script");
40553
40554 // more advanced tabs, built from javascript
40555 var jtabs = new Roo.TabPanel("jtabs");
40556 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40557
40558 // set up the UpdateManager
40559 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40560 var updater = tab2.getUpdateManager();
40561 updater.setDefaultUrl("ajax1.htm");
40562 tab2.on('activate', updater.refresh, updater, true);
40563
40564 // Use setUrl for Ajax loading
40565 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40566 tab3.setUrl("ajax2.htm", null, true);
40567
40568 // Disabled tab
40569 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40570 tab4.disable();
40571
40572 jtabs.activate("jtabs-1");
40573  * </code></pre>
40574  * @constructor
40575  * Create a new TabPanel.
40576  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40577  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40578  */
40579 Roo.bootstrap.panel.Tabs = function(config){
40580     /**
40581     * The container element for this TabPanel.
40582     * @type Roo.Element
40583     */
40584     this.el = Roo.get(config.el);
40585     delete config.el;
40586     if(config){
40587         if(typeof config == "boolean"){
40588             this.tabPosition = config ? "bottom" : "top";
40589         }else{
40590             Roo.apply(this, config);
40591         }
40592     }
40593     
40594     if(this.tabPosition == "bottom"){
40595         // if tabs are at the bottom = create the body first.
40596         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40597         this.el.addClass("roo-tabs-bottom");
40598     }
40599     // next create the tabs holders
40600     
40601     if (this.tabPosition == "west"){
40602         
40603         var reg = this.region; // fake it..
40604         while (reg) {
40605             if (!reg.mgr.parent) {
40606                 break;
40607             }
40608             reg = reg.mgr.parent.region;
40609         }
40610         Roo.log("got nest?");
40611         Roo.log(reg);
40612         if (reg.mgr.getRegion('west')) {
40613             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40614             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40615             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40616             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40617             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40618         
40619             
40620         }
40621         
40622         
40623     } else {
40624      
40625         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40626         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40627         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40628         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40629     }
40630     
40631     
40632     if(Roo.isIE){
40633         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40634     }
40635     
40636     // finally - if tabs are at the top, then create the body last..
40637     if(this.tabPosition != "bottom"){
40638         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40639          * @type Roo.Element
40640          */
40641         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40642         this.el.addClass("roo-tabs-top");
40643     }
40644     this.items = [];
40645
40646     this.bodyEl.setStyle("position", "relative");
40647
40648     this.active = null;
40649     this.activateDelegate = this.activate.createDelegate(this);
40650
40651     this.addEvents({
40652         /**
40653          * @event tabchange
40654          * Fires when the active tab changes
40655          * @param {Roo.TabPanel} this
40656          * @param {Roo.TabPanelItem} activePanel The new active tab
40657          */
40658         "tabchange": true,
40659         /**
40660          * @event beforetabchange
40661          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40662          * @param {Roo.TabPanel} this
40663          * @param {Object} e Set cancel to true on this object to cancel the tab change
40664          * @param {Roo.TabPanelItem} tab The tab being changed to
40665          */
40666         "beforetabchange" : true
40667     });
40668
40669     Roo.EventManager.onWindowResize(this.onResize, this);
40670     this.cpad = this.el.getPadding("lr");
40671     this.hiddenCount = 0;
40672
40673
40674     // toolbar on the tabbar support...
40675     if (this.toolbar) {
40676         alert("no toolbar support yet");
40677         this.toolbar  = false;
40678         /*
40679         var tcfg = this.toolbar;
40680         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40681         this.toolbar = new Roo.Toolbar(tcfg);
40682         if (Roo.isSafari) {
40683             var tbl = tcfg.container.child('table', true);
40684             tbl.setAttribute('width', '100%');
40685         }
40686         */
40687         
40688     }
40689    
40690
40691
40692     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40693 };
40694
40695 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40696     /*
40697      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40698      */
40699     tabPosition : "top",
40700     /*
40701      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40702      */
40703     currentTabWidth : 0,
40704     /*
40705      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40706      */
40707     minTabWidth : 40,
40708     /*
40709      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40710      */
40711     maxTabWidth : 250,
40712     /*
40713      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40714      */
40715     preferredTabWidth : 175,
40716     /*
40717      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40718      */
40719     resizeTabs : false,
40720     /*
40721      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40722      */
40723     monitorResize : true,
40724     /*
40725      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40726      */
40727     toolbar : false,  // set by caller..
40728     
40729     region : false, /// set by caller
40730     
40731     disableTooltips : true, // not used yet...
40732
40733     /**
40734      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40735      * @param {String} id The id of the div to use <b>or create</b>
40736      * @param {String} text The text for the tab
40737      * @param {String} content (optional) Content to put in the TabPanelItem body
40738      * @param {Boolean} closable (optional) True to create a close icon on the tab
40739      * @return {Roo.TabPanelItem} The created TabPanelItem
40740      */
40741     addTab : function(id, text, content, closable, tpl)
40742     {
40743         var item = new Roo.bootstrap.panel.TabItem({
40744             panel: this,
40745             id : id,
40746             text : text,
40747             closable : closable,
40748             tpl : tpl
40749         });
40750         this.addTabItem(item);
40751         if(content){
40752             item.setContent(content);
40753         }
40754         return item;
40755     },
40756
40757     /**
40758      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40759      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40760      * @return {Roo.TabPanelItem}
40761      */
40762     getTab : function(id){
40763         return this.items[id];
40764     },
40765
40766     /**
40767      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40768      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40769      */
40770     hideTab : function(id){
40771         var t = this.items[id];
40772         if(!t.isHidden()){
40773            t.setHidden(true);
40774            this.hiddenCount++;
40775            this.autoSizeTabs();
40776         }
40777     },
40778
40779     /**
40780      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40781      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40782      */
40783     unhideTab : function(id){
40784         var t = this.items[id];
40785         if(t.isHidden()){
40786            t.setHidden(false);
40787            this.hiddenCount--;
40788            this.autoSizeTabs();
40789         }
40790     },
40791
40792     /**
40793      * Adds an existing {@link Roo.TabPanelItem}.
40794      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40795      */
40796     addTabItem : function(item)
40797     {
40798         this.items[item.id] = item;
40799         this.items.push(item);
40800         this.autoSizeTabs();
40801       //  if(this.resizeTabs){
40802     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40803   //         this.autoSizeTabs();
40804 //        }else{
40805 //            item.autoSize();
40806        // }
40807     },
40808
40809     /**
40810      * Removes a {@link Roo.TabPanelItem}.
40811      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40812      */
40813     removeTab : function(id){
40814         var items = this.items;
40815         var tab = items[id];
40816         if(!tab) { return; }
40817         var index = items.indexOf(tab);
40818         if(this.active == tab && items.length > 1){
40819             var newTab = this.getNextAvailable(index);
40820             if(newTab) {
40821                 newTab.activate();
40822             }
40823         }
40824         this.stripEl.dom.removeChild(tab.pnode.dom);
40825         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40826             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40827         }
40828         items.splice(index, 1);
40829         delete this.items[tab.id];
40830         tab.fireEvent("close", tab);
40831         tab.purgeListeners();
40832         this.autoSizeTabs();
40833     },
40834
40835     getNextAvailable : function(start){
40836         var items = this.items;
40837         var index = start;
40838         // look for a next tab that will slide over to
40839         // replace the one being removed
40840         while(index < items.length){
40841             var item = items[++index];
40842             if(item && !item.isHidden()){
40843                 return item;
40844             }
40845         }
40846         // if one isn't found select the previous tab (on the left)
40847         index = start;
40848         while(index >= 0){
40849             var item = items[--index];
40850             if(item && !item.isHidden()){
40851                 return item;
40852             }
40853         }
40854         return null;
40855     },
40856
40857     /**
40858      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40859      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40860      */
40861     disableTab : function(id){
40862         var tab = this.items[id];
40863         if(tab && this.active != tab){
40864             tab.disable();
40865         }
40866     },
40867
40868     /**
40869      * Enables a {@link Roo.TabPanelItem} that is disabled.
40870      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40871      */
40872     enableTab : function(id){
40873         var tab = this.items[id];
40874         tab.enable();
40875     },
40876
40877     /**
40878      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40879      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40880      * @return {Roo.TabPanelItem} The TabPanelItem.
40881      */
40882     activate : function(id)
40883     {
40884         //Roo.log('activite:'  + id);
40885         
40886         var tab = this.items[id];
40887         if(!tab){
40888             return null;
40889         }
40890         if(tab == this.active || tab.disabled){
40891             return tab;
40892         }
40893         var e = {};
40894         this.fireEvent("beforetabchange", this, e, tab);
40895         if(e.cancel !== true && !tab.disabled){
40896             if(this.active){
40897                 this.active.hide();
40898             }
40899             this.active = this.items[id];
40900             this.active.show();
40901             this.fireEvent("tabchange", this, this.active);
40902         }
40903         return tab;
40904     },
40905
40906     /**
40907      * Gets the active {@link Roo.TabPanelItem}.
40908      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40909      */
40910     getActiveTab : function(){
40911         return this.active;
40912     },
40913
40914     /**
40915      * Updates the tab body element to fit the height of the container element
40916      * for overflow scrolling
40917      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40918      */
40919     syncHeight : function(targetHeight){
40920         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40921         var bm = this.bodyEl.getMargins();
40922         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40923         this.bodyEl.setHeight(newHeight);
40924         return newHeight;
40925     },
40926
40927     onResize : function(){
40928         if(this.monitorResize){
40929             this.autoSizeTabs();
40930         }
40931     },
40932
40933     /**
40934      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40935      */
40936     beginUpdate : function(){
40937         this.updating = true;
40938     },
40939
40940     /**
40941      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40942      */
40943     endUpdate : function(){
40944         this.updating = false;
40945         this.autoSizeTabs();
40946     },
40947
40948     /**
40949      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40950      */
40951     autoSizeTabs : function()
40952     {
40953         var count = this.items.length;
40954         var vcount = count - this.hiddenCount;
40955         
40956         if (vcount < 2) {
40957             this.stripEl.hide();
40958         } else {
40959             this.stripEl.show();
40960         }
40961         
40962         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40963             return;
40964         }
40965         
40966         
40967         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40968         var availWidth = Math.floor(w / vcount);
40969         var b = this.stripBody;
40970         if(b.getWidth() > w){
40971             var tabs = this.items;
40972             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40973             if(availWidth < this.minTabWidth){
40974                 /*if(!this.sleft){    // incomplete scrolling code
40975                     this.createScrollButtons();
40976                 }
40977                 this.showScroll();
40978                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40979             }
40980         }else{
40981             if(this.currentTabWidth < this.preferredTabWidth){
40982                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40983             }
40984         }
40985     },
40986
40987     /**
40988      * Returns the number of tabs in this TabPanel.
40989      * @return {Number}
40990      */
40991      getCount : function(){
40992          return this.items.length;
40993      },
40994
40995     /**
40996      * Resizes all the tabs to the passed width
40997      * @param {Number} The new width
40998      */
40999     setTabWidth : function(width){
41000         this.currentTabWidth = width;
41001         for(var i = 0, len = this.items.length; i < len; i++) {
41002                 if(!this.items[i].isHidden()) {
41003                 this.items[i].setWidth(width);
41004             }
41005         }
41006     },
41007
41008     /**
41009      * Destroys this TabPanel
41010      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41011      */
41012     destroy : function(removeEl){
41013         Roo.EventManager.removeResizeListener(this.onResize, this);
41014         for(var i = 0, len = this.items.length; i < len; i++){
41015             this.items[i].purgeListeners();
41016         }
41017         if(removeEl === true){
41018             this.el.update("");
41019             this.el.remove();
41020         }
41021     },
41022     
41023     createStrip : function(container)
41024     {
41025         var strip = document.createElement("nav");
41026         strip.className = Roo.bootstrap.version == 4 ?
41027             "navbar-light bg-light" : 
41028             "navbar navbar-default"; //"x-tabs-wrap";
41029         container.appendChild(strip);
41030         return strip;
41031     },
41032     
41033     createStripList : function(strip)
41034     {
41035         // div wrapper for retard IE
41036         // returns the "tr" element.
41037         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41038         //'<div class="x-tabs-strip-wrap">'+
41039           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41040           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41041         return strip.firstChild; //.firstChild.firstChild.firstChild;
41042     },
41043     createBody : function(container)
41044     {
41045         var body = document.createElement("div");
41046         Roo.id(body, "tab-body");
41047         //Roo.fly(body).addClass("x-tabs-body");
41048         Roo.fly(body).addClass("tab-content");
41049         container.appendChild(body);
41050         return body;
41051     },
41052     createItemBody :function(bodyEl, id){
41053         var body = Roo.getDom(id);
41054         if(!body){
41055             body = document.createElement("div");
41056             body.id = id;
41057         }
41058         //Roo.fly(body).addClass("x-tabs-item-body");
41059         Roo.fly(body).addClass("tab-pane");
41060          bodyEl.insertBefore(body, bodyEl.firstChild);
41061         return body;
41062     },
41063     /** @private */
41064     createStripElements :  function(stripEl, text, closable, tpl)
41065     {
41066         var td = document.createElement("li"); // was td..
41067         td.className = 'nav-item';
41068         
41069         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41070         
41071         
41072         stripEl.appendChild(td);
41073         /*if(closable){
41074             td.className = "x-tabs-closable";
41075             if(!this.closeTpl){
41076                 this.closeTpl = new Roo.Template(
41077                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41078                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41079                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41080                 );
41081             }
41082             var el = this.closeTpl.overwrite(td, {"text": text});
41083             var close = el.getElementsByTagName("div")[0];
41084             var inner = el.getElementsByTagName("em")[0];
41085             return {"el": el, "close": close, "inner": inner};
41086         } else {
41087         */
41088         // not sure what this is..
41089 //            if(!this.tabTpl){
41090                 //this.tabTpl = new Roo.Template(
41091                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41092                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41093                 //);
41094 //                this.tabTpl = new Roo.Template(
41095 //                   '<a href="#">' +
41096 //                   '<span unselectable="on"' +
41097 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41098 //                            ' >{text}</span></a>'
41099 //                );
41100 //                
41101 //            }
41102
41103
41104             var template = tpl || this.tabTpl || false;
41105             
41106             if(!template){
41107                 template =  new Roo.Template(
41108                         Roo.bootstrap.version == 4 ? 
41109                             (
41110                                 '<a class="nav-link" href="#" unselectable="on"' +
41111                                      (this.disableTooltips ? '' : ' title="{text}"') +
41112                                      ' >{text}</a>'
41113                             ) : (
41114                                 '<a class="nav-link" href="#">' +
41115                                 '<span unselectable="on"' +
41116                                          (this.disableTooltips ? '' : ' title="{text}"') +
41117                                     ' >{text}</span></a>'
41118                             )
41119                 );
41120             }
41121             
41122             switch (typeof(template)) {
41123                 case 'object' :
41124                     break;
41125                 case 'string' :
41126                     template = new Roo.Template(template);
41127                     break;
41128                 default :
41129                     break;
41130             }
41131             
41132             var el = template.overwrite(td, {"text": text});
41133             
41134             var inner = el.getElementsByTagName("span")[0];
41135             
41136             return {"el": el, "inner": inner};
41137             
41138     }
41139         
41140     
41141 });
41142
41143 /**
41144  * @class Roo.TabPanelItem
41145  * @extends Roo.util.Observable
41146  * Represents an individual item (tab plus body) in a TabPanel.
41147  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41148  * @param {String} id The id of this TabPanelItem
41149  * @param {String} text The text for the tab of this TabPanelItem
41150  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41151  */
41152 Roo.bootstrap.panel.TabItem = function(config){
41153     /**
41154      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41155      * @type Roo.TabPanel
41156      */
41157     this.tabPanel = config.panel;
41158     /**
41159      * The id for this TabPanelItem
41160      * @type String
41161      */
41162     this.id = config.id;
41163     /** @private */
41164     this.disabled = false;
41165     /** @private */
41166     this.text = config.text;
41167     /** @private */
41168     this.loaded = false;
41169     this.closable = config.closable;
41170
41171     /**
41172      * The body element for this TabPanelItem.
41173      * @type Roo.Element
41174      */
41175     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41176     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41177     this.bodyEl.setStyle("display", "block");
41178     this.bodyEl.setStyle("zoom", "1");
41179     //this.hideAction();
41180
41181     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41182     /** @private */
41183     this.el = Roo.get(els.el);
41184     this.inner = Roo.get(els.inner, true);
41185      this.textEl = Roo.bootstrap.version == 4 ?
41186         this.el : Roo.get(this.el.dom.firstChild, true);
41187
41188     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41189     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41190
41191     
41192 //    this.el.on("mousedown", this.onTabMouseDown, this);
41193     this.el.on("click", this.onTabClick, this);
41194     /** @private */
41195     if(config.closable){
41196         var c = Roo.get(els.close, true);
41197         c.dom.title = this.closeText;
41198         c.addClassOnOver("close-over");
41199         c.on("click", this.closeClick, this);
41200      }
41201
41202     this.addEvents({
41203          /**
41204          * @event activate
41205          * Fires when this tab becomes the active tab.
41206          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41207          * @param {Roo.TabPanelItem} this
41208          */
41209         "activate": true,
41210         /**
41211          * @event beforeclose
41212          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41213          * @param {Roo.TabPanelItem} this
41214          * @param {Object} e Set cancel to true on this object to cancel the close.
41215          */
41216         "beforeclose": true,
41217         /**
41218          * @event close
41219          * Fires when this tab is closed.
41220          * @param {Roo.TabPanelItem} this
41221          */
41222          "close": true,
41223         /**
41224          * @event deactivate
41225          * Fires when this tab is no longer the active tab.
41226          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41227          * @param {Roo.TabPanelItem} this
41228          */
41229          "deactivate" : true
41230     });
41231     this.hidden = false;
41232
41233     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41234 };
41235
41236 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41237            {
41238     purgeListeners : function(){
41239        Roo.util.Observable.prototype.purgeListeners.call(this);
41240        this.el.removeAllListeners();
41241     },
41242     /**
41243      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41244      */
41245     show : function(){
41246         this.status_node.addClass("active");
41247         this.showAction();
41248         if(Roo.isOpera){
41249             this.tabPanel.stripWrap.repaint();
41250         }
41251         this.fireEvent("activate", this.tabPanel, this);
41252     },
41253
41254     /**
41255      * Returns true if this tab is the active tab.
41256      * @return {Boolean}
41257      */
41258     isActive : function(){
41259         return this.tabPanel.getActiveTab() == this;
41260     },
41261
41262     /**
41263      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41264      */
41265     hide : function(){
41266         this.status_node.removeClass("active");
41267         this.hideAction();
41268         this.fireEvent("deactivate", this.tabPanel, this);
41269     },
41270
41271     hideAction : function(){
41272         this.bodyEl.hide();
41273         this.bodyEl.setStyle("position", "absolute");
41274         this.bodyEl.setLeft("-20000px");
41275         this.bodyEl.setTop("-20000px");
41276     },
41277
41278     showAction : function(){
41279         this.bodyEl.setStyle("position", "relative");
41280         this.bodyEl.setTop("");
41281         this.bodyEl.setLeft("");
41282         this.bodyEl.show();
41283     },
41284
41285     /**
41286      * Set the tooltip for the tab.
41287      * @param {String} tooltip The tab's tooltip
41288      */
41289     setTooltip : function(text){
41290         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41291             this.textEl.dom.qtip = text;
41292             this.textEl.dom.removeAttribute('title');
41293         }else{
41294             this.textEl.dom.title = text;
41295         }
41296     },
41297
41298     onTabClick : function(e){
41299         e.preventDefault();
41300         this.tabPanel.activate(this.id);
41301     },
41302
41303     onTabMouseDown : function(e){
41304         e.preventDefault();
41305         this.tabPanel.activate(this.id);
41306     },
41307 /*
41308     getWidth : function(){
41309         return this.inner.getWidth();
41310     },
41311
41312     setWidth : function(width){
41313         var iwidth = width - this.linode.getPadding("lr");
41314         this.inner.setWidth(iwidth);
41315         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41316         this.linode.setWidth(width);
41317     },
41318 */
41319     /**
41320      * Show or hide the tab
41321      * @param {Boolean} hidden True to hide or false to show.
41322      */
41323     setHidden : function(hidden){
41324         this.hidden = hidden;
41325         this.linode.setStyle("display", hidden ? "none" : "");
41326     },
41327
41328     /**
41329      * Returns true if this tab is "hidden"
41330      * @return {Boolean}
41331      */
41332     isHidden : function(){
41333         return this.hidden;
41334     },
41335
41336     /**
41337      * Returns the text for this tab
41338      * @return {String}
41339      */
41340     getText : function(){
41341         return this.text;
41342     },
41343     /*
41344     autoSize : function(){
41345         //this.el.beginMeasure();
41346         this.textEl.setWidth(1);
41347         /*
41348          *  #2804 [new] Tabs in Roojs
41349          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41350          */
41351         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41352         //this.el.endMeasure();
41353     //},
41354
41355     /**
41356      * Sets the text for the tab (Note: this also sets the tooltip text)
41357      * @param {String} text The tab's text and tooltip
41358      */
41359     setText : function(text){
41360         this.text = text;
41361         this.textEl.update(text);
41362         this.setTooltip(text);
41363         //if(!this.tabPanel.resizeTabs){
41364         //    this.autoSize();
41365         //}
41366     },
41367     /**
41368      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41369      */
41370     activate : function(){
41371         this.tabPanel.activate(this.id);
41372     },
41373
41374     /**
41375      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41376      */
41377     disable : function(){
41378         if(this.tabPanel.active != this){
41379             this.disabled = true;
41380             this.status_node.addClass("disabled");
41381         }
41382     },
41383
41384     /**
41385      * Enables this TabPanelItem if it was previously disabled.
41386      */
41387     enable : function(){
41388         this.disabled = false;
41389         this.status_node.removeClass("disabled");
41390     },
41391
41392     /**
41393      * Sets the content for this TabPanelItem.
41394      * @param {String} content The content
41395      * @param {Boolean} loadScripts true to look for and load scripts
41396      */
41397     setContent : function(content, loadScripts){
41398         this.bodyEl.update(content, loadScripts);
41399     },
41400
41401     /**
41402      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41403      * @return {Roo.UpdateManager} The UpdateManager
41404      */
41405     getUpdateManager : function(){
41406         return this.bodyEl.getUpdateManager();
41407     },
41408
41409     /**
41410      * Set a URL to be used to load the content for this TabPanelItem.
41411      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41412      * @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)
41413      * @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)
41414      * @return {Roo.UpdateManager} The UpdateManager
41415      */
41416     setUrl : function(url, params, loadOnce){
41417         if(this.refreshDelegate){
41418             this.un('activate', this.refreshDelegate);
41419         }
41420         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41421         this.on("activate", this.refreshDelegate);
41422         return this.bodyEl.getUpdateManager();
41423     },
41424
41425     /** @private */
41426     _handleRefresh : function(url, params, loadOnce){
41427         if(!loadOnce || !this.loaded){
41428             var updater = this.bodyEl.getUpdateManager();
41429             updater.update(url, params, this._setLoaded.createDelegate(this));
41430         }
41431     },
41432
41433     /**
41434      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41435      *   Will fail silently if the setUrl method has not been called.
41436      *   This does not activate the panel, just updates its content.
41437      */
41438     refresh : function(){
41439         if(this.refreshDelegate){
41440            this.loaded = false;
41441            this.refreshDelegate();
41442         }
41443     },
41444
41445     /** @private */
41446     _setLoaded : function(){
41447         this.loaded = true;
41448     },
41449
41450     /** @private */
41451     closeClick : function(e){
41452         var o = {};
41453         e.stopEvent();
41454         this.fireEvent("beforeclose", this, o);
41455         if(o.cancel !== true){
41456             this.tabPanel.removeTab(this.id);
41457         }
41458     },
41459     /**
41460      * The text displayed in the tooltip for the close icon.
41461      * @type String
41462      */
41463     closeText : "Close this tab"
41464 });
41465 /**
41466 *    This script refer to:
41467 *    Title: International Telephone Input
41468 *    Author: Jack O'Connor
41469 *    Code version:  v12.1.12
41470 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41471 **/
41472
41473 Roo.bootstrap.PhoneInputData = function() {
41474     var d = [
41475       [
41476         "Afghanistan (‫افغانستان‬‎)",
41477         "af",
41478         "93"
41479       ],
41480       [
41481         "Albania (Shqipëri)",
41482         "al",
41483         "355"
41484       ],
41485       [
41486         "Algeria (‫الجزائر‬‎)",
41487         "dz",
41488         "213"
41489       ],
41490       [
41491         "American Samoa",
41492         "as",
41493         "1684"
41494       ],
41495       [
41496         "Andorra",
41497         "ad",
41498         "376"
41499       ],
41500       [
41501         "Angola",
41502         "ao",
41503         "244"
41504       ],
41505       [
41506         "Anguilla",
41507         "ai",
41508         "1264"
41509       ],
41510       [
41511         "Antigua and Barbuda",
41512         "ag",
41513         "1268"
41514       ],
41515       [
41516         "Argentina",
41517         "ar",
41518         "54"
41519       ],
41520       [
41521         "Armenia (Հայաստան)",
41522         "am",
41523         "374"
41524       ],
41525       [
41526         "Aruba",
41527         "aw",
41528         "297"
41529       ],
41530       [
41531         "Australia",
41532         "au",
41533         "61",
41534         0
41535       ],
41536       [
41537         "Austria (Österreich)",
41538         "at",
41539         "43"
41540       ],
41541       [
41542         "Azerbaijan (Azərbaycan)",
41543         "az",
41544         "994"
41545       ],
41546       [
41547         "Bahamas",
41548         "bs",
41549         "1242"
41550       ],
41551       [
41552         "Bahrain (‫البحرين‬‎)",
41553         "bh",
41554         "973"
41555       ],
41556       [
41557         "Bangladesh (বাংলাদেশ)",
41558         "bd",
41559         "880"
41560       ],
41561       [
41562         "Barbados",
41563         "bb",
41564         "1246"
41565       ],
41566       [
41567         "Belarus (Беларусь)",
41568         "by",
41569         "375"
41570       ],
41571       [
41572         "Belgium (België)",
41573         "be",
41574         "32"
41575       ],
41576       [
41577         "Belize",
41578         "bz",
41579         "501"
41580       ],
41581       [
41582         "Benin (Bénin)",
41583         "bj",
41584         "229"
41585       ],
41586       [
41587         "Bermuda",
41588         "bm",
41589         "1441"
41590       ],
41591       [
41592         "Bhutan (འབྲུག)",
41593         "bt",
41594         "975"
41595       ],
41596       [
41597         "Bolivia",
41598         "bo",
41599         "591"
41600       ],
41601       [
41602         "Bosnia and Herzegovina (Босна и Херцеговина)",
41603         "ba",
41604         "387"
41605       ],
41606       [
41607         "Botswana",
41608         "bw",
41609         "267"
41610       ],
41611       [
41612         "Brazil (Brasil)",
41613         "br",
41614         "55"
41615       ],
41616       [
41617         "British Indian Ocean Territory",
41618         "io",
41619         "246"
41620       ],
41621       [
41622         "British Virgin Islands",
41623         "vg",
41624         "1284"
41625       ],
41626       [
41627         "Brunei",
41628         "bn",
41629         "673"
41630       ],
41631       [
41632         "Bulgaria (България)",
41633         "bg",
41634         "359"
41635       ],
41636       [
41637         "Burkina Faso",
41638         "bf",
41639         "226"
41640       ],
41641       [
41642         "Burundi (Uburundi)",
41643         "bi",
41644         "257"
41645       ],
41646       [
41647         "Cambodia (កម្ពុជា)",
41648         "kh",
41649         "855"
41650       ],
41651       [
41652         "Cameroon (Cameroun)",
41653         "cm",
41654         "237"
41655       ],
41656       [
41657         "Canada",
41658         "ca",
41659         "1",
41660         1,
41661         ["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"]
41662       ],
41663       [
41664         "Cape Verde (Kabu Verdi)",
41665         "cv",
41666         "238"
41667       ],
41668       [
41669         "Caribbean Netherlands",
41670         "bq",
41671         "599",
41672         1
41673       ],
41674       [
41675         "Cayman Islands",
41676         "ky",
41677         "1345"
41678       ],
41679       [
41680         "Central African Republic (République centrafricaine)",
41681         "cf",
41682         "236"
41683       ],
41684       [
41685         "Chad (Tchad)",
41686         "td",
41687         "235"
41688       ],
41689       [
41690         "Chile",
41691         "cl",
41692         "56"
41693       ],
41694       [
41695         "China (中国)",
41696         "cn",
41697         "86"
41698       ],
41699       [
41700         "Christmas Island",
41701         "cx",
41702         "61",
41703         2
41704       ],
41705       [
41706         "Cocos (Keeling) Islands",
41707         "cc",
41708         "61",
41709         1
41710       ],
41711       [
41712         "Colombia",
41713         "co",
41714         "57"
41715       ],
41716       [
41717         "Comoros (‫جزر القمر‬‎)",
41718         "km",
41719         "269"
41720       ],
41721       [
41722         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41723         "cd",
41724         "243"
41725       ],
41726       [
41727         "Congo (Republic) (Congo-Brazzaville)",
41728         "cg",
41729         "242"
41730       ],
41731       [
41732         "Cook Islands",
41733         "ck",
41734         "682"
41735       ],
41736       [
41737         "Costa Rica",
41738         "cr",
41739         "506"
41740       ],
41741       [
41742         "Côte d’Ivoire",
41743         "ci",
41744         "225"
41745       ],
41746       [
41747         "Croatia (Hrvatska)",
41748         "hr",
41749         "385"
41750       ],
41751       [
41752         "Cuba",
41753         "cu",
41754         "53"
41755       ],
41756       [
41757         "Curaçao",
41758         "cw",
41759         "599",
41760         0
41761       ],
41762       [
41763         "Cyprus (Κύπρος)",
41764         "cy",
41765         "357"
41766       ],
41767       [
41768         "Czech Republic (Česká republika)",
41769         "cz",
41770         "420"
41771       ],
41772       [
41773         "Denmark (Danmark)",
41774         "dk",
41775         "45"
41776       ],
41777       [
41778         "Djibouti",
41779         "dj",
41780         "253"
41781       ],
41782       [
41783         "Dominica",
41784         "dm",
41785         "1767"
41786       ],
41787       [
41788         "Dominican Republic (República Dominicana)",
41789         "do",
41790         "1",
41791         2,
41792         ["809", "829", "849"]
41793       ],
41794       [
41795         "Ecuador",
41796         "ec",
41797         "593"
41798       ],
41799       [
41800         "Egypt (‫مصر‬‎)",
41801         "eg",
41802         "20"
41803       ],
41804       [
41805         "El Salvador",
41806         "sv",
41807         "503"
41808       ],
41809       [
41810         "Equatorial Guinea (Guinea Ecuatorial)",
41811         "gq",
41812         "240"
41813       ],
41814       [
41815         "Eritrea",
41816         "er",
41817         "291"
41818       ],
41819       [
41820         "Estonia (Eesti)",
41821         "ee",
41822         "372"
41823       ],
41824       [
41825         "Ethiopia",
41826         "et",
41827         "251"
41828       ],
41829       [
41830         "Falkland Islands (Islas Malvinas)",
41831         "fk",
41832         "500"
41833       ],
41834       [
41835         "Faroe Islands (Føroyar)",
41836         "fo",
41837         "298"
41838       ],
41839       [
41840         "Fiji",
41841         "fj",
41842         "679"
41843       ],
41844       [
41845         "Finland (Suomi)",
41846         "fi",
41847         "358",
41848         0
41849       ],
41850       [
41851         "France",
41852         "fr",
41853         "33"
41854       ],
41855       [
41856         "French Guiana (Guyane française)",
41857         "gf",
41858         "594"
41859       ],
41860       [
41861         "French Polynesia (Polynésie française)",
41862         "pf",
41863         "689"
41864       ],
41865       [
41866         "Gabon",
41867         "ga",
41868         "241"
41869       ],
41870       [
41871         "Gambia",
41872         "gm",
41873         "220"
41874       ],
41875       [
41876         "Georgia (საქართველო)",
41877         "ge",
41878         "995"
41879       ],
41880       [
41881         "Germany (Deutschland)",
41882         "de",
41883         "49"
41884       ],
41885       [
41886         "Ghana (Gaana)",
41887         "gh",
41888         "233"
41889       ],
41890       [
41891         "Gibraltar",
41892         "gi",
41893         "350"
41894       ],
41895       [
41896         "Greece (Ελλάδα)",
41897         "gr",
41898         "30"
41899       ],
41900       [
41901         "Greenland (Kalaallit Nunaat)",
41902         "gl",
41903         "299"
41904       ],
41905       [
41906         "Grenada",
41907         "gd",
41908         "1473"
41909       ],
41910       [
41911         "Guadeloupe",
41912         "gp",
41913         "590",
41914         0
41915       ],
41916       [
41917         "Guam",
41918         "gu",
41919         "1671"
41920       ],
41921       [
41922         "Guatemala",
41923         "gt",
41924         "502"
41925       ],
41926       [
41927         "Guernsey",
41928         "gg",
41929         "44",
41930         1
41931       ],
41932       [
41933         "Guinea (Guinée)",
41934         "gn",
41935         "224"
41936       ],
41937       [
41938         "Guinea-Bissau (Guiné Bissau)",
41939         "gw",
41940         "245"
41941       ],
41942       [
41943         "Guyana",
41944         "gy",
41945         "592"
41946       ],
41947       [
41948         "Haiti",
41949         "ht",
41950         "509"
41951       ],
41952       [
41953         "Honduras",
41954         "hn",
41955         "504"
41956       ],
41957       [
41958         "Hong Kong (香港)",
41959         "hk",
41960         "852"
41961       ],
41962       [
41963         "Hungary (Magyarország)",
41964         "hu",
41965         "36"
41966       ],
41967       [
41968         "Iceland (Ísland)",
41969         "is",
41970         "354"
41971       ],
41972       [
41973         "India (भारत)",
41974         "in",
41975         "91"
41976       ],
41977       [
41978         "Indonesia",
41979         "id",
41980         "62"
41981       ],
41982       [
41983         "Iran (‫ایران‬‎)",
41984         "ir",
41985         "98"
41986       ],
41987       [
41988         "Iraq (‫العراق‬‎)",
41989         "iq",
41990         "964"
41991       ],
41992       [
41993         "Ireland",
41994         "ie",
41995         "353"
41996       ],
41997       [
41998         "Isle of Man",
41999         "im",
42000         "44",
42001         2
42002       ],
42003       [
42004         "Israel (‫ישראל‬‎)",
42005         "il",
42006         "972"
42007       ],
42008       [
42009         "Italy (Italia)",
42010         "it",
42011         "39",
42012         0
42013       ],
42014       [
42015         "Jamaica",
42016         "jm",
42017         "1876"
42018       ],
42019       [
42020         "Japan (日本)",
42021         "jp",
42022         "81"
42023       ],
42024       [
42025         "Jersey",
42026         "je",
42027         "44",
42028         3
42029       ],
42030       [
42031         "Jordan (‫الأردن‬‎)",
42032         "jo",
42033         "962"
42034       ],
42035       [
42036         "Kazakhstan (Казахстан)",
42037         "kz",
42038         "7",
42039         1
42040       ],
42041       [
42042         "Kenya",
42043         "ke",
42044         "254"
42045       ],
42046       [
42047         "Kiribati",
42048         "ki",
42049         "686"
42050       ],
42051       [
42052         "Kosovo",
42053         "xk",
42054         "383"
42055       ],
42056       [
42057         "Kuwait (‫الكويت‬‎)",
42058         "kw",
42059         "965"
42060       ],
42061       [
42062         "Kyrgyzstan (Кыргызстан)",
42063         "kg",
42064         "996"
42065       ],
42066       [
42067         "Laos (ລາວ)",
42068         "la",
42069         "856"
42070       ],
42071       [
42072         "Latvia (Latvija)",
42073         "lv",
42074         "371"
42075       ],
42076       [
42077         "Lebanon (‫لبنان‬‎)",
42078         "lb",
42079         "961"
42080       ],
42081       [
42082         "Lesotho",
42083         "ls",
42084         "266"
42085       ],
42086       [
42087         "Liberia",
42088         "lr",
42089         "231"
42090       ],
42091       [
42092         "Libya (‫ليبيا‬‎)",
42093         "ly",
42094         "218"
42095       ],
42096       [
42097         "Liechtenstein",
42098         "li",
42099         "423"
42100       ],
42101       [
42102         "Lithuania (Lietuva)",
42103         "lt",
42104         "370"
42105       ],
42106       [
42107         "Luxembourg",
42108         "lu",
42109         "352"
42110       ],
42111       [
42112         "Macau (澳門)",
42113         "mo",
42114         "853"
42115       ],
42116       [
42117         "Macedonia (FYROM) (Македонија)",
42118         "mk",
42119         "389"
42120       ],
42121       [
42122         "Madagascar (Madagasikara)",
42123         "mg",
42124         "261"
42125       ],
42126       [
42127         "Malawi",
42128         "mw",
42129         "265"
42130       ],
42131       [
42132         "Malaysia",
42133         "my",
42134         "60"
42135       ],
42136       [
42137         "Maldives",
42138         "mv",
42139         "960"
42140       ],
42141       [
42142         "Mali",
42143         "ml",
42144         "223"
42145       ],
42146       [
42147         "Malta",
42148         "mt",
42149         "356"
42150       ],
42151       [
42152         "Marshall Islands",
42153         "mh",
42154         "692"
42155       ],
42156       [
42157         "Martinique",
42158         "mq",
42159         "596"
42160       ],
42161       [
42162         "Mauritania (‫موريتانيا‬‎)",
42163         "mr",
42164         "222"
42165       ],
42166       [
42167         "Mauritius (Moris)",
42168         "mu",
42169         "230"
42170       ],
42171       [
42172         "Mayotte",
42173         "yt",
42174         "262",
42175         1
42176       ],
42177       [
42178         "Mexico (México)",
42179         "mx",
42180         "52"
42181       ],
42182       [
42183         "Micronesia",
42184         "fm",
42185         "691"
42186       ],
42187       [
42188         "Moldova (Republica Moldova)",
42189         "md",
42190         "373"
42191       ],
42192       [
42193         "Monaco",
42194         "mc",
42195         "377"
42196       ],
42197       [
42198         "Mongolia (Монгол)",
42199         "mn",
42200         "976"
42201       ],
42202       [
42203         "Montenegro (Crna Gora)",
42204         "me",
42205         "382"
42206       ],
42207       [
42208         "Montserrat",
42209         "ms",
42210         "1664"
42211       ],
42212       [
42213         "Morocco (‫المغرب‬‎)",
42214         "ma",
42215         "212",
42216         0
42217       ],
42218       [
42219         "Mozambique (Moçambique)",
42220         "mz",
42221         "258"
42222       ],
42223       [
42224         "Myanmar (Burma) (မြန်မာ)",
42225         "mm",
42226         "95"
42227       ],
42228       [
42229         "Namibia (Namibië)",
42230         "na",
42231         "264"
42232       ],
42233       [
42234         "Nauru",
42235         "nr",
42236         "674"
42237       ],
42238       [
42239         "Nepal (नेपाल)",
42240         "np",
42241         "977"
42242       ],
42243       [
42244         "Netherlands (Nederland)",
42245         "nl",
42246         "31"
42247       ],
42248       [
42249         "New Caledonia (Nouvelle-Calédonie)",
42250         "nc",
42251         "687"
42252       ],
42253       [
42254         "New Zealand",
42255         "nz",
42256         "64"
42257       ],
42258       [
42259         "Nicaragua",
42260         "ni",
42261         "505"
42262       ],
42263       [
42264         "Niger (Nijar)",
42265         "ne",
42266         "227"
42267       ],
42268       [
42269         "Nigeria",
42270         "ng",
42271         "234"
42272       ],
42273       [
42274         "Niue",
42275         "nu",
42276         "683"
42277       ],
42278       [
42279         "Norfolk Island",
42280         "nf",
42281         "672"
42282       ],
42283       [
42284         "North Korea (조선 민주주의 인민 공화국)",
42285         "kp",
42286         "850"
42287       ],
42288       [
42289         "Northern Mariana Islands",
42290         "mp",
42291         "1670"
42292       ],
42293       [
42294         "Norway (Norge)",
42295         "no",
42296         "47",
42297         0
42298       ],
42299       [
42300         "Oman (‫عُمان‬‎)",
42301         "om",
42302         "968"
42303       ],
42304       [
42305         "Pakistan (‫پاکستان‬‎)",
42306         "pk",
42307         "92"
42308       ],
42309       [
42310         "Palau",
42311         "pw",
42312         "680"
42313       ],
42314       [
42315         "Palestine (‫فلسطين‬‎)",
42316         "ps",
42317         "970"
42318       ],
42319       [
42320         "Panama (Panamá)",
42321         "pa",
42322         "507"
42323       ],
42324       [
42325         "Papua New Guinea",
42326         "pg",
42327         "675"
42328       ],
42329       [
42330         "Paraguay",
42331         "py",
42332         "595"
42333       ],
42334       [
42335         "Peru (Perú)",
42336         "pe",
42337         "51"
42338       ],
42339       [
42340         "Philippines",
42341         "ph",
42342         "63"
42343       ],
42344       [
42345         "Poland (Polska)",
42346         "pl",
42347         "48"
42348       ],
42349       [
42350         "Portugal",
42351         "pt",
42352         "351"
42353       ],
42354       [
42355         "Puerto Rico",
42356         "pr",
42357         "1",
42358         3,
42359         ["787", "939"]
42360       ],
42361       [
42362         "Qatar (‫قطر‬‎)",
42363         "qa",
42364         "974"
42365       ],
42366       [
42367         "Réunion (La Réunion)",
42368         "re",
42369         "262",
42370         0
42371       ],
42372       [
42373         "Romania (România)",
42374         "ro",
42375         "40"
42376       ],
42377       [
42378         "Russia (Россия)",
42379         "ru",
42380         "7",
42381         0
42382       ],
42383       [
42384         "Rwanda",
42385         "rw",
42386         "250"
42387       ],
42388       [
42389         "Saint Barthélemy",
42390         "bl",
42391         "590",
42392         1
42393       ],
42394       [
42395         "Saint Helena",
42396         "sh",
42397         "290"
42398       ],
42399       [
42400         "Saint Kitts and Nevis",
42401         "kn",
42402         "1869"
42403       ],
42404       [
42405         "Saint Lucia",
42406         "lc",
42407         "1758"
42408       ],
42409       [
42410         "Saint Martin (Saint-Martin (partie française))",
42411         "mf",
42412         "590",
42413         2
42414       ],
42415       [
42416         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42417         "pm",
42418         "508"
42419       ],
42420       [
42421         "Saint Vincent and the Grenadines",
42422         "vc",
42423         "1784"
42424       ],
42425       [
42426         "Samoa",
42427         "ws",
42428         "685"
42429       ],
42430       [
42431         "San Marino",
42432         "sm",
42433         "378"
42434       ],
42435       [
42436         "São Tomé and Príncipe (São Tomé e Príncipe)",
42437         "st",
42438         "239"
42439       ],
42440       [
42441         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42442         "sa",
42443         "966"
42444       ],
42445       [
42446         "Senegal (Sénégal)",
42447         "sn",
42448         "221"
42449       ],
42450       [
42451         "Serbia (Србија)",
42452         "rs",
42453         "381"
42454       ],
42455       [
42456         "Seychelles",
42457         "sc",
42458         "248"
42459       ],
42460       [
42461         "Sierra Leone",
42462         "sl",
42463         "232"
42464       ],
42465       [
42466         "Singapore",
42467         "sg",
42468         "65"
42469       ],
42470       [
42471         "Sint Maarten",
42472         "sx",
42473         "1721"
42474       ],
42475       [
42476         "Slovakia (Slovensko)",
42477         "sk",
42478         "421"
42479       ],
42480       [
42481         "Slovenia (Slovenija)",
42482         "si",
42483         "386"
42484       ],
42485       [
42486         "Solomon Islands",
42487         "sb",
42488         "677"
42489       ],
42490       [
42491         "Somalia (Soomaaliya)",
42492         "so",
42493         "252"
42494       ],
42495       [
42496         "South Africa",
42497         "za",
42498         "27"
42499       ],
42500       [
42501         "South Korea (대한민국)",
42502         "kr",
42503         "82"
42504       ],
42505       [
42506         "South Sudan (‫جنوب السودان‬‎)",
42507         "ss",
42508         "211"
42509       ],
42510       [
42511         "Spain (España)",
42512         "es",
42513         "34"
42514       ],
42515       [
42516         "Sri Lanka (ශ්‍රී ලංකාව)",
42517         "lk",
42518         "94"
42519       ],
42520       [
42521         "Sudan (‫السودان‬‎)",
42522         "sd",
42523         "249"
42524       ],
42525       [
42526         "Suriname",
42527         "sr",
42528         "597"
42529       ],
42530       [
42531         "Svalbard and Jan Mayen",
42532         "sj",
42533         "47",
42534         1
42535       ],
42536       [
42537         "Swaziland",
42538         "sz",
42539         "268"
42540       ],
42541       [
42542         "Sweden (Sverige)",
42543         "se",
42544         "46"
42545       ],
42546       [
42547         "Switzerland (Schweiz)",
42548         "ch",
42549         "41"
42550       ],
42551       [
42552         "Syria (‫سوريا‬‎)",
42553         "sy",
42554         "963"
42555       ],
42556       [
42557         "Taiwan (台灣)",
42558         "tw",
42559         "886"
42560       ],
42561       [
42562         "Tajikistan",
42563         "tj",
42564         "992"
42565       ],
42566       [
42567         "Tanzania",
42568         "tz",
42569         "255"
42570       ],
42571       [
42572         "Thailand (ไทย)",
42573         "th",
42574         "66"
42575       ],
42576       [
42577         "Timor-Leste",
42578         "tl",
42579         "670"
42580       ],
42581       [
42582         "Togo",
42583         "tg",
42584         "228"
42585       ],
42586       [
42587         "Tokelau",
42588         "tk",
42589         "690"
42590       ],
42591       [
42592         "Tonga",
42593         "to",
42594         "676"
42595       ],
42596       [
42597         "Trinidad and Tobago",
42598         "tt",
42599         "1868"
42600       ],
42601       [
42602         "Tunisia (‫تونس‬‎)",
42603         "tn",
42604         "216"
42605       ],
42606       [
42607         "Turkey (Türkiye)",
42608         "tr",
42609         "90"
42610       ],
42611       [
42612         "Turkmenistan",
42613         "tm",
42614         "993"
42615       ],
42616       [
42617         "Turks and Caicos Islands",
42618         "tc",
42619         "1649"
42620       ],
42621       [
42622         "Tuvalu",
42623         "tv",
42624         "688"
42625       ],
42626       [
42627         "U.S. Virgin Islands",
42628         "vi",
42629         "1340"
42630       ],
42631       [
42632         "Uganda",
42633         "ug",
42634         "256"
42635       ],
42636       [
42637         "Ukraine (Україна)",
42638         "ua",
42639         "380"
42640       ],
42641       [
42642         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42643         "ae",
42644         "971"
42645       ],
42646       [
42647         "United Kingdom",
42648         "gb",
42649         "44",
42650         0
42651       ],
42652       [
42653         "United States",
42654         "us",
42655         "1",
42656         0
42657       ],
42658       [
42659         "Uruguay",
42660         "uy",
42661         "598"
42662       ],
42663       [
42664         "Uzbekistan (Oʻzbekiston)",
42665         "uz",
42666         "998"
42667       ],
42668       [
42669         "Vanuatu",
42670         "vu",
42671         "678"
42672       ],
42673       [
42674         "Vatican City (Città del Vaticano)",
42675         "va",
42676         "39",
42677         1
42678       ],
42679       [
42680         "Venezuela",
42681         "ve",
42682         "58"
42683       ],
42684       [
42685         "Vietnam (Việt Nam)",
42686         "vn",
42687         "84"
42688       ],
42689       [
42690         "Wallis and Futuna (Wallis-et-Futuna)",
42691         "wf",
42692         "681"
42693       ],
42694       [
42695         "Western Sahara (‫الصحراء الغربية‬‎)",
42696         "eh",
42697         "212",
42698         1
42699       ],
42700       [
42701         "Yemen (‫اليمن‬‎)",
42702         "ye",
42703         "967"
42704       ],
42705       [
42706         "Zambia",
42707         "zm",
42708         "260"
42709       ],
42710       [
42711         "Zimbabwe",
42712         "zw",
42713         "263"
42714       ],
42715       [
42716         "Åland Islands",
42717         "ax",
42718         "358",
42719         1
42720       ]
42721   ];
42722   
42723   return d;
42724 }/**
42725 *    This script refer to:
42726 *    Title: International Telephone Input
42727 *    Author: Jack O'Connor
42728 *    Code version:  v12.1.12
42729 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42730 **/
42731
42732 /**
42733  * @class Roo.bootstrap.PhoneInput
42734  * @extends Roo.bootstrap.TriggerField
42735  * An input with International dial-code selection
42736  
42737  * @cfg {String} defaultDialCode default '+852'
42738  * @cfg {Array} preferedCountries default []
42739   
42740  * @constructor
42741  * Create a new PhoneInput.
42742  * @param {Object} config Configuration options
42743  */
42744
42745 Roo.bootstrap.PhoneInput = function(config) {
42746     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42747 };
42748
42749 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42750         
42751         listWidth: undefined,
42752         
42753         selectedClass: 'active',
42754         
42755         invalidClass : "has-warning",
42756         
42757         validClass: 'has-success',
42758         
42759         allowed: '0123456789',
42760         
42761         max_length: 15,
42762         
42763         /**
42764          * @cfg {String} defaultDialCode The default dial code when initializing the input
42765          */
42766         defaultDialCode: '+852',
42767         
42768         /**
42769          * @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
42770          */
42771         preferedCountries: false,
42772         
42773         getAutoCreate : function()
42774         {
42775             var data = Roo.bootstrap.PhoneInputData();
42776             var align = this.labelAlign || this.parentLabelAlign();
42777             var id = Roo.id();
42778             
42779             this.allCountries = [];
42780             this.dialCodeMapping = [];
42781             
42782             for (var i = 0; i < data.length; i++) {
42783               var c = data[i];
42784               this.allCountries[i] = {
42785                 name: c[0],
42786                 iso2: c[1],
42787                 dialCode: c[2],
42788                 priority: c[3] || 0,
42789                 areaCodes: c[4] || null
42790               };
42791               this.dialCodeMapping[c[2]] = {
42792                   name: c[0],
42793                   iso2: c[1],
42794                   priority: c[3] || 0,
42795                   areaCodes: c[4] || null
42796               };
42797             }
42798             
42799             var cfg = {
42800                 cls: 'form-group',
42801                 cn: []
42802             };
42803             
42804             var input =  {
42805                 tag: 'input',
42806                 id : id,
42807                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42808                 maxlength: this.max_length,
42809                 cls : 'form-control tel-input',
42810                 autocomplete: 'new-password'
42811             };
42812             
42813             var hiddenInput = {
42814                 tag: 'input',
42815                 type: 'hidden',
42816                 cls: 'hidden-tel-input'
42817             };
42818             
42819             if (this.name) {
42820                 hiddenInput.name = this.name;
42821             }
42822             
42823             if (this.disabled) {
42824                 input.disabled = true;
42825             }
42826             
42827             var flag_container = {
42828                 tag: 'div',
42829                 cls: 'flag-box',
42830                 cn: [
42831                     {
42832                         tag: 'div',
42833                         cls: 'flag'
42834                     },
42835                     {
42836                         tag: 'div',
42837                         cls: 'caret'
42838                     }
42839                 ]
42840             };
42841             
42842             var box = {
42843                 tag: 'div',
42844                 cls: this.hasFeedback ? 'has-feedback' : '',
42845                 cn: [
42846                     hiddenInput,
42847                     input,
42848                     {
42849                         tag: 'input',
42850                         cls: 'dial-code-holder',
42851                         disabled: true
42852                     }
42853                 ]
42854             };
42855             
42856             var container = {
42857                 cls: 'roo-select2-container input-group',
42858                 cn: [
42859                     flag_container,
42860                     box
42861                 ]
42862             };
42863             
42864             if (this.fieldLabel.length) {
42865                 var indicator = {
42866                     tag: 'i',
42867                     tooltip: 'This field is required'
42868                 };
42869                 
42870                 var label = {
42871                     tag: 'label',
42872                     'for':  id,
42873                     cls: 'control-label',
42874                     cn: []
42875                 };
42876                 
42877                 var label_text = {
42878                     tag: 'span',
42879                     html: this.fieldLabel
42880                 };
42881                 
42882                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42883                 label.cn = [
42884                     indicator,
42885                     label_text
42886                 ];
42887                 
42888                 if(this.indicatorpos == 'right') {
42889                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42890                     label.cn = [
42891                         label_text,
42892                         indicator
42893                     ];
42894                 }
42895                 
42896                 if(align == 'left') {
42897                     container = {
42898                         tag: 'div',
42899                         cn: [
42900                             container
42901                         ]
42902                     };
42903                     
42904                     if(this.labelWidth > 12){
42905                         label.style = "width: " + this.labelWidth + 'px';
42906                     }
42907                     if(this.labelWidth < 13 && this.labelmd == 0){
42908                         this.labelmd = this.labelWidth;
42909                     }
42910                     if(this.labellg > 0){
42911                         label.cls += ' col-lg-' + this.labellg;
42912                         input.cls += ' col-lg-' + (12 - this.labellg);
42913                     }
42914                     if(this.labelmd > 0){
42915                         label.cls += ' col-md-' + this.labelmd;
42916                         container.cls += ' col-md-' + (12 - this.labelmd);
42917                     }
42918                     if(this.labelsm > 0){
42919                         label.cls += ' col-sm-' + this.labelsm;
42920                         container.cls += ' col-sm-' + (12 - this.labelsm);
42921                     }
42922                     if(this.labelxs > 0){
42923                         label.cls += ' col-xs-' + this.labelxs;
42924                         container.cls += ' col-xs-' + (12 - this.labelxs);
42925                     }
42926                 }
42927             }
42928             
42929             cfg.cn = [
42930                 label,
42931                 container
42932             ];
42933             
42934             var settings = this;
42935             
42936             ['xs','sm','md','lg'].map(function(size){
42937                 if (settings[size]) {
42938                     cfg.cls += ' col-' + size + '-' + settings[size];
42939                 }
42940             });
42941             
42942             this.store = new Roo.data.Store({
42943                 proxy : new Roo.data.MemoryProxy({}),
42944                 reader : new Roo.data.JsonReader({
42945                     fields : [
42946                         {
42947                             'name' : 'name',
42948                             'type' : 'string'
42949                         },
42950                         {
42951                             'name' : 'iso2',
42952                             'type' : 'string'
42953                         },
42954                         {
42955                             'name' : 'dialCode',
42956                             'type' : 'string'
42957                         },
42958                         {
42959                             'name' : 'priority',
42960                             'type' : 'string'
42961                         },
42962                         {
42963                             'name' : 'areaCodes',
42964                             'type' : 'string'
42965                         }
42966                     ]
42967                 })
42968             });
42969             
42970             if(!this.preferedCountries) {
42971                 this.preferedCountries = [
42972                     'hk',
42973                     'gb',
42974                     'us'
42975                 ];
42976             }
42977             
42978             var p = this.preferedCountries.reverse();
42979             
42980             if(p) {
42981                 for (var i = 0; i < p.length; i++) {
42982                     for (var j = 0; j < this.allCountries.length; j++) {
42983                         if(this.allCountries[j].iso2 == p[i]) {
42984                             var t = this.allCountries[j];
42985                             this.allCountries.splice(j,1);
42986                             this.allCountries.unshift(t);
42987                         }
42988                     } 
42989                 }
42990             }
42991             
42992             this.store.proxy.data = {
42993                 success: true,
42994                 data: this.allCountries
42995             };
42996             
42997             return cfg;
42998         },
42999         
43000         initEvents : function()
43001         {
43002             this.createList();
43003             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43004             
43005             this.indicator = this.indicatorEl();
43006             this.flag = this.flagEl();
43007             this.dialCodeHolder = this.dialCodeHolderEl();
43008             
43009             this.trigger = this.el.select('div.flag-box',true).first();
43010             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43011             
43012             var _this = this;
43013             
43014             (function(){
43015                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43016                 _this.list.setWidth(lw);
43017             }).defer(100);
43018             
43019             this.list.on('mouseover', this.onViewOver, this);
43020             this.list.on('mousemove', this.onViewMove, this);
43021             this.inputEl().on("keyup", this.onKeyUp, this);
43022             this.inputEl().on("keypress", this.onKeyPress, this);
43023             
43024             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43025
43026             this.view = new Roo.View(this.list, this.tpl, {
43027                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43028             });
43029             
43030             this.view.on('click', this.onViewClick, this);
43031             this.setValue(this.defaultDialCode);
43032         },
43033         
43034         onTriggerClick : function(e)
43035         {
43036             Roo.log('trigger click');
43037             if(this.disabled){
43038                 return;
43039             }
43040             
43041             if(this.isExpanded()){
43042                 this.collapse();
43043                 this.hasFocus = false;
43044             }else {
43045                 this.store.load({});
43046                 this.hasFocus = true;
43047                 this.expand();
43048             }
43049         },
43050         
43051         isExpanded : function()
43052         {
43053             return this.list.isVisible();
43054         },
43055         
43056         collapse : function()
43057         {
43058             if(!this.isExpanded()){
43059                 return;
43060             }
43061             this.list.hide();
43062             Roo.get(document).un('mousedown', this.collapseIf, this);
43063             Roo.get(document).un('mousewheel', this.collapseIf, this);
43064             this.fireEvent('collapse', this);
43065             this.validate();
43066         },
43067         
43068         expand : function()
43069         {
43070             Roo.log('expand');
43071
43072             if(this.isExpanded() || !this.hasFocus){
43073                 return;
43074             }
43075             
43076             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43077             this.list.setWidth(lw);
43078             
43079             this.list.show();
43080             this.restrictHeight();
43081             
43082             Roo.get(document).on('mousedown', this.collapseIf, this);
43083             Roo.get(document).on('mousewheel', this.collapseIf, this);
43084             
43085             this.fireEvent('expand', this);
43086         },
43087         
43088         restrictHeight : function()
43089         {
43090             this.list.alignTo(this.inputEl(), this.listAlign);
43091             this.list.alignTo(this.inputEl(), this.listAlign);
43092         },
43093         
43094         onViewOver : function(e, t)
43095         {
43096             if(this.inKeyMode){
43097                 return;
43098             }
43099             var item = this.view.findItemFromChild(t);
43100             
43101             if(item){
43102                 var index = this.view.indexOf(item);
43103                 this.select(index, false);
43104             }
43105         },
43106
43107         // private
43108         onViewClick : function(view, doFocus, el, e)
43109         {
43110             var index = this.view.getSelectedIndexes()[0];
43111             
43112             var r = this.store.getAt(index);
43113             
43114             if(r){
43115                 this.onSelect(r, index);
43116             }
43117             if(doFocus !== false && !this.blockFocus){
43118                 this.inputEl().focus();
43119             }
43120         },
43121         
43122         onViewMove : function(e, t)
43123         {
43124             this.inKeyMode = false;
43125         },
43126         
43127         select : function(index, scrollIntoView)
43128         {
43129             this.selectedIndex = index;
43130             this.view.select(index);
43131             if(scrollIntoView !== false){
43132                 var el = this.view.getNode(index);
43133                 if(el){
43134                     this.list.scrollChildIntoView(el, false);
43135                 }
43136             }
43137         },
43138         
43139         createList : function()
43140         {
43141             this.list = Roo.get(document.body).createChild({
43142                 tag: 'ul',
43143                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43144                 style: 'display:none'
43145             });
43146             
43147             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43148         },
43149         
43150         collapseIf : function(e)
43151         {
43152             var in_combo  = e.within(this.el);
43153             var in_list =  e.within(this.list);
43154             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43155             
43156             if (in_combo || in_list || is_list) {
43157                 return;
43158             }
43159             this.collapse();
43160         },
43161         
43162         onSelect : function(record, index)
43163         {
43164             if(this.fireEvent('beforeselect', this, record, index) !== false){
43165                 
43166                 this.setFlagClass(record.data.iso2);
43167                 this.setDialCode(record.data.dialCode);
43168                 this.hasFocus = false;
43169                 this.collapse();
43170                 this.fireEvent('select', this, record, index);
43171             }
43172         },
43173         
43174         flagEl : function()
43175         {
43176             var flag = this.el.select('div.flag',true).first();
43177             if(!flag){
43178                 return false;
43179             }
43180             return flag;
43181         },
43182         
43183         dialCodeHolderEl : function()
43184         {
43185             var d = this.el.select('input.dial-code-holder',true).first();
43186             if(!d){
43187                 return false;
43188             }
43189             return d;
43190         },
43191         
43192         setDialCode : function(v)
43193         {
43194             this.dialCodeHolder.dom.value = '+'+v;
43195         },
43196         
43197         setFlagClass : function(n)
43198         {
43199             this.flag.dom.className = 'flag '+n;
43200         },
43201         
43202         getValue : function()
43203         {
43204             var v = this.inputEl().getValue();
43205             if(this.dialCodeHolder) {
43206                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43207             }
43208             return v;
43209         },
43210         
43211         setValue : function(v)
43212         {
43213             var d = this.getDialCode(v);
43214             
43215             //invalid dial code
43216             if(v.length == 0 || !d || d.length == 0) {
43217                 if(this.rendered){
43218                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43219                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43220                 }
43221                 return;
43222             }
43223             
43224             //valid dial code
43225             this.setFlagClass(this.dialCodeMapping[d].iso2);
43226             this.setDialCode(d);
43227             this.inputEl().dom.value = v.replace('+'+d,'');
43228             this.hiddenEl().dom.value = this.getValue();
43229             
43230             this.validate();
43231         },
43232         
43233         getDialCode : function(v)
43234         {
43235             v = v ||  '';
43236             
43237             if (v.length == 0) {
43238                 return this.dialCodeHolder.dom.value;
43239             }
43240             
43241             var dialCode = "";
43242             if (v.charAt(0) != "+") {
43243                 return false;
43244             }
43245             var numericChars = "";
43246             for (var i = 1; i < v.length; i++) {
43247               var c = v.charAt(i);
43248               if (!isNaN(c)) {
43249                 numericChars += c;
43250                 if (this.dialCodeMapping[numericChars]) {
43251                   dialCode = v.substr(1, i);
43252                 }
43253                 if (numericChars.length == 4) {
43254                   break;
43255                 }
43256               }
43257             }
43258             return dialCode;
43259         },
43260         
43261         reset : function()
43262         {
43263             this.setValue(this.defaultDialCode);
43264             this.validate();
43265         },
43266         
43267         hiddenEl : function()
43268         {
43269             return this.el.select('input.hidden-tel-input',true).first();
43270         },
43271         
43272         // after setting val
43273         onKeyUp : function(e){
43274             this.setValue(this.getValue());
43275         },
43276         
43277         onKeyPress : function(e){
43278             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43279                 e.stopEvent();
43280             }
43281         }
43282         
43283 });
43284 /**
43285  * @class Roo.bootstrap.MoneyField
43286  * @extends Roo.bootstrap.ComboBox
43287  * Bootstrap MoneyField class
43288  * 
43289  * @constructor
43290  * Create a new MoneyField.
43291  * @param {Object} config Configuration options
43292  */
43293
43294 Roo.bootstrap.MoneyField = function(config) {
43295     
43296     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43297     
43298 };
43299
43300 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43301     
43302     /**
43303      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43304      */
43305     allowDecimals : true,
43306     /**
43307      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43308      */
43309     decimalSeparator : ".",
43310     /**
43311      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43312      */
43313     decimalPrecision : 0,
43314     /**
43315      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43316      */
43317     allowNegative : true,
43318     /**
43319      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43320      */
43321     allowZero: true,
43322     /**
43323      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43324      */
43325     minValue : Number.NEGATIVE_INFINITY,
43326     /**
43327      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43328      */
43329     maxValue : Number.MAX_VALUE,
43330     /**
43331      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43332      */
43333     minText : "The minimum value for this field is {0}",
43334     /**
43335      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43336      */
43337     maxText : "The maximum value for this field is {0}",
43338     /**
43339      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43340      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43341      */
43342     nanText : "{0} is not a valid number",
43343     /**
43344      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43345      */
43346     castInt : true,
43347     /**
43348      * @cfg {String} defaults currency of the MoneyField
43349      * value should be in lkey
43350      */
43351     defaultCurrency : false,
43352     /**
43353      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43354      */
43355     thousandsDelimiter : false,
43356     /**
43357      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43358      */
43359     max_length: false,
43360     
43361     inputlg : 9,
43362     inputmd : 9,
43363     inputsm : 9,
43364     inputxs : 6,
43365     
43366     store : false,
43367     
43368     getAutoCreate : function()
43369     {
43370         var align = this.labelAlign || this.parentLabelAlign();
43371         
43372         var id = Roo.id();
43373
43374         var cfg = {
43375             cls: 'form-group',
43376             cn: []
43377         };
43378
43379         var input =  {
43380             tag: 'input',
43381             id : id,
43382             cls : 'form-control roo-money-amount-input',
43383             autocomplete: 'new-password'
43384         };
43385         
43386         var hiddenInput = {
43387             tag: 'input',
43388             type: 'hidden',
43389             id: Roo.id(),
43390             cls: 'hidden-number-input'
43391         };
43392         
43393         if(this.max_length) {
43394             input.maxlength = this.max_length; 
43395         }
43396         
43397         if (this.name) {
43398             hiddenInput.name = this.name;
43399         }
43400
43401         if (this.disabled) {
43402             input.disabled = true;
43403         }
43404
43405         var clg = 12 - this.inputlg;
43406         var cmd = 12 - this.inputmd;
43407         var csm = 12 - this.inputsm;
43408         var cxs = 12 - this.inputxs;
43409         
43410         var container = {
43411             tag : 'div',
43412             cls : 'row roo-money-field',
43413             cn : [
43414                 {
43415                     tag : 'div',
43416                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43417                     cn : [
43418                         {
43419                             tag : 'div',
43420                             cls: 'roo-select2-container input-group',
43421                             cn: [
43422                                 {
43423                                     tag : 'input',
43424                                     cls : 'form-control roo-money-currency-input',
43425                                     autocomplete: 'new-password',
43426                                     readOnly : 1,
43427                                     name : this.currencyName
43428                                 },
43429                                 {
43430                                     tag :'span',
43431                                     cls : 'input-group-addon',
43432                                     cn : [
43433                                         {
43434                                             tag: 'span',
43435                                             cls: 'caret'
43436                                         }
43437                                     ]
43438                                 }
43439                             ]
43440                         }
43441                     ]
43442                 },
43443                 {
43444                     tag : 'div',
43445                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43446                     cn : [
43447                         {
43448                             tag: 'div',
43449                             cls: this.hasFeedback ? 'has-feedback' : '',
43450                             cn: [
43451                                 input
43452                             ]
43453                         }
43454                     ]
43455                 }
43456             ]
43457             
43458         };
43459         
43460         if (this.fieldLabel.length) {
43461             var indicator = {
43462                 tag: 'i',
43463                 tooltip: 'This field is required'
43464             };
43465
43466             var label = {
43467                 tag: 'label',
43468                 'for':  id,
43469                 cls: 'control-label',
43470                 cn: []
43471             };
43472
43473             var label_text = {
43474                 tag: 'span',
43475                 html: this.fieldLabel
43476             };
43477
43478             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43479             label.cn = [
43480                 indicator,
43481                 label_text
43482             ];
43483
43484             if(this.indicatorpos == 'right') {
43485                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43486                 label.cn = [
43487                     label_text,
43488                     indicator
43489                 ];
43490             }
43491
43492             if(align == 'left') {
43493                 container = {
43494                     tag: 'div',
43495                     cn: [
43496                         container
43497                     ]
43498                 };
43499
43500                 if(this.labelWidth > 12){
43501                     label.style = "width: " + this.labelWidth + 'px';
43502                 }
43503                 if(this.labelWidth < 13 && this.labelmd == 0){
43504                     this.labelmd = this.labelWidth;
43505                 }
43506                 if(this.labellg > 0){
43507                     label.cls += ' col-lg-' + this.labellg;
43508                     input.cls += ' col-lg-' + (12 - this.labellg);
43509                 }
43510                 if(this.labelmd > 0){
43511                     label.cls += ' col-md-' + this.labelmd;
43512                     container.cls += ' col-md-' + (12 - this.labelmd);
43513                 }
43514                 if(this.labelsm > 0){
43515                     label.cls += ' col-sm-' + this.labelsm;
43516                     container.cls += ' col-sm-' + (12 - this.labelsm);
43517                 }
43518                 if(this.labelxs > 0){
43519                     label.cls += ' col-xs-' + this.labelxs;
43520                     container.cls += ' col-xs-' + (12 - this.labelxs);
43521                 }
43522             }
43523         }
43524
43525         cfg.cn = [
43526             label,
43527             container,
43528             hiddenInput
43529         ];
43530         
43531         var settings = this;
43532
43533         ['xs','sm','md','lg'].map(function(size){
43534             if (settings[size]) {
43535                 cfg.cls += ' col-' + size + '-' + settings[size];
43536             }
43537         });
43538         
43539         return cfg;
43540     },
43541     
43542     initEvents : function()
43543     {
43544         this.indicator = this.indicatorEl();
43545         
43546         this.initCurrencyEvent();
43547         
43548         this.initNumberEvent();
43549     },
43550     
43551     initCurrencyEvent : function()
43552     {
43553         if (!this.store) {
43554             throw "can not find store for combo";
43555         }
43556         
43557         this.store = Roo.factory(this.store, Roo.data);
43558         this.store.parent = this;
43559         
43560         this.createList();
43561         
43562         this.triggerEl = this.el.select('.input-group-addon', true).first();
43563         
43564         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43565         
43566         var _this = this;
43567         
43568         (function(){
43569             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43570             _this.list.setWidth(lw);
43571         }).defer(100);
43572         
43573         this.list.on('mouseover', this.onViewOver, this);
43574         this.list.on('mousemove', this.onViewMove, this);
43575         this.list.on('scroll', this.onViewScroll, this);
43576         
43577         if(!this.tpl){
43578             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43579         }
43580         
43581         this.view = new Roo.View(this.list, this.tpl, {
43582             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43583         });
43584         
43585         this.view.on('click', this.onViewClick, this);
43586         
43587         this.store.on('beforeload', this.onBeforeLoad, this);
43588         this.store.on('load', this.onLoad, this);
43589         this.store.on('loadexception', this.onLoadException, this);
43590         
43591         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43592             "up" : function(e){
43593                 this.inKeyMode = true;
43594                 this.selectPrev();
43595             },
43596
43597             "down" : function(e){
43598                 if(!this.isExpanded()){
43599                     this.onTriggerClick();
43600                 }else{
43601                     this.inKeyMode = true;
43602                     this.selectNext();
43603                 }
43604             },
43605
43606             "enter" : function(e){
43607                 this.collapse();
43608                 
43609                 if(this.fireEvent("specialkey", this, e)){
43610                     this.onViewClick(false);
43611                 }
43612                 
43613                 return true;
43614             },
43615
43616             "esc" : function(e){
43617                 this.collapse();
43618             },
43619
43620             "tab" : function(e){
43621                 this.collapse();
43622                 
43623                 if(this.fireEvent("specialkey", this, e)){
43624                     this.onViewClick(false);
43625                 }
43626                 
43627                 return true;
43628             },
43629
43630             scope : this,
43631
43632             doRelay : function(foo, bar, hname){
43633                 if(hname == 'down' || this.scope.isExpanded()){
43634                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43635                 }
43636                 return true;
43637             },
43638
43639             forceKeyDown: true
43640         });
43641         
43642         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43643         
43644     },
43645     
43646     initNumberEvent : function(e)
43647     {
43648         this.inputEl().on("keydown" , this.fireKey,  this);
43649         this.inputEl().on("focus", this.onFocus,  this);
43650         this.inputEl().on("blur", this.onBlur,  this);
43651         
43652         this.inputEl().relayEvent('keyup', this);
43653         
43654         if(this.indicator){
43655             this.indicator.addClass('invisible');
43656         }
43657  
43658         this.originalValue = this.getValue();
43659         
43660         if(this.validationEvent == 'keyup'){
43661             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43662             this.inputEl().on('keyup', this.filterValidation, this);
43663         }
43664         else if(this.validationEvent !== false){
43665             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43666         }
43667         
43668         if(this.selectOnFocus){
43669             this.on("focus", this.preFocus, this);
43670             
43671         }
43672         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43673             this.inputEl().on("keypress", this.filterKeys, this);
43674         } else {
43675             this.inputEl().relayEvent('keypress', this);
43676         }
43677         
43678         var allowed = "0123456789";
43679         
43680         if(this.allowDecimals){
43681             allowed += this.decimalSeparator;
43682         }
43683         
43684         if(this.allowNegative){
43685             allowed += "-";
43686         }
43687         
43688         if(this.thousandsDelimiter) {
43689             allowed += ",";
43690         }
43691         
43692         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43693         
43694         var keyPress = function(e){
43695             
43696             var k = e.getKey();
43697             
43698             var c = e.getCharCode();
43699             
43700             if(
43701                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43702                     allowed.indexOf(String.fromCharCode(c)) === -1
43703             ){
43704                 e.stopEvent();
43705                 return;
43706             }
43707             
43708             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43709                 return;
43710             }
43711             
43712             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43713                 e.stopEvent();
43714             }
43715         };
43716         
43717         this.inputEl().on("keypress", keyPress, this);
43718         
43719     },
43720     
43721     onTriggerClick : function(e)
43722     {   
43723         if(this.disabled){
43724             return;
43725         }
43726         
43727         this.page = 0;
43728         this.loadNext = false;
43729         
43730         if(this.isExpanded()){
43731             this.collapse();
43732             return;
43733         }
43734         
43735         this.hasFocus = true;
43736         
43737         if(this.triggerAction == 'all') {
43738             this.doQuery(this.allQuery, true);
43739             return;
43740         }
43741         
43742         this.doQuery(this.getRawValue());
43743     },
43744     
43745     getCurrency : function()
43746     {   
43747         var v = this.currencyEl().getValue();
43748         
43749         return v;
43750     },
43751     
43752     restrictHeight : function()
43753     {
43754         this.list.alignTo(this.currencyEl(), this.listAlign);
43755         this.list.alignTo(this.currencyEl(), this.listAlign);
43756     },
43757     
43758     onViewClick : function(view, doFocus, el, e)
43759     {
43760         var index = this.view.getSelectedIndexes()[0];
43761         
43762         var r = this.store.getAt(index);
43763         
43764         if(r){
43765             this.onSelect(r, index);
43766         }
43767     },
43768     
43769     onSelect : function(record, index){
43770         
43771         if(this.fireEvent('beforeselect', this, record, index) !== false){
43772         
43773             this.setFromCurrencyData(index > -1 ? record.data : false);
43774             
43775             this.collapse();
43776             
43777             this.fireEvent('select', this, record, index);
43778         }
43779     },
43780     
43781     setFromCurrencyData : function(o)
43782     {
43783         var currency = '';
43784         
43785         this.lastCurrency = o;
43786         
43787         if (this.currencyField) {
43788             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43789         } else {
43790             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43791         }
43792         
43793         this.lastSelectionText = currency;
43794         
43795         //setting default currency
43796         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43797             this.setCurrency(this.defaultCurrency);
43798             return;
43799         }
43800         
43801         this.setCurrency(currency);
43802     },
43803     
43804     setFromData : function(o)
43805     {
43806         var c = {};
43807         
43808         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43809         
43810         this.setFromCurrencyData(c);
43811         
43812         var value = '';
43813         
43814         if (this.name) {
43815             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43816         } else {
43817             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43818         }
43819         
43820         this.setValue(value);
43821         
43822     },
43823     
43824     setCurrency : function(v)
43825     {   
43826         this.currencyValue = v;
43827         
43828         if(this.rendered){
43829             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43830             this.validate();
43831         }
43832     },
43833     
43834     setValue : function(v)
43835     {
43836         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43837         
43838         this.value = v;
43839         
43840         if(this.rendered){
43841             
43842             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43843             
43844             this.inputEl().dom.value = (v == '') ? '' :
43845                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43846             
43847             if(!this.allowZero && v === '0') {
43848                 this.hiddenEl().dom.value = '';
43849                 this.inputEl().dom.value = '';
43850             }
43851             
43852             this.validate();
43853         }
43854     },
43855     
43856     getRawValue : function()
43857     {
43858         var v = this.inputEl().getValue();
43859         
43860         return v;
43861     },
43862     
43863     getValue : function()
43864     {
43865         return this.fixPrecision(this.parseValue(this.getRawValue()));
43866     },
43867     
43868     parseValue : function(value)
43869     {
43870         if(this.thousandsDelimiter) {
43871             value += "";
43872             r = new RegExp(",", "g");
43873             value = value.replace(r, "");
43874         }
43875         
43876         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43877         return isNaN(value) ? '' : value;
43878         
43879     },
43880     
43881     fixPrecision : function(value)
43882     {
43883         if(this.thousandsDelimiter) {
43884             value += "";
43885             r = new RegExp(",", "g");
43886             value = value.replace(r, "");
43887         }
43888         
43889         var nan = isNaN(value);
43890         
43891         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43892             return nan ? '' : value;
43893         }
43894         return parseFloat(value).toFixed(this.decimalPrecision);
43895     },
43896     
43897     decimalPrecisionFcn : function(v)
43898     {
43899         return Math.floor(v);
43900     },
43901     
43902     validateValue : function(value)
43903     {
43904         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43905             return false;
43906         }
43907         
43908         var num = this.parseValue(value);
43909         
43910         if(isNaN(num)){
43911             this.markInvalid(String.format(this.nanText, value));
43912             return false;
43913         }
43914         
43915         if(num < this.minValue){
43916             this.markInvalid(String.format(this.minText, this.minValue));
43917             return false;
43918         }
43919         
43920         if(num > this.maxValue){
43921             this.markInvalid(String.format(this.maxText, this.maxValue));
43922             return false;
43923         }
43924         
43925         return true;
43926     },
43927     
43928     validate : function()
43929     {
43930         if(this.disabled || this.allowBlank){
43931             this.markValid();
43932             return true;
43933         }
43934         
43935         var currency = this.getCurrency();
43936         
43937         if(this.validateValue(this.getRawValue()) && currency.length){
43938             this.markValid();
43939             return true;
43940         }
43941         
43942         this.markInvalid();
43943         return false;
43944     },
43945     
43946     getName: function()
43947     {
43948         return this.name;
43949     },
43950     
43951     beforeBlur : function()
43952     {
43953         if(!this.castInt){
43954             return;
43955         }
43956         
43957         var v = this.parseValue(this.getRawValue());
43958         
43959         if(v || v == 0){
43960             this.setValue(v);
43961         }
43962     },
43963     
43964     onBlur : function()
43965     {
43966         this.beforeBlur();
43967         
43968         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43969             //this.el.removeClass(this.focusClass);
43970         }
43971         
43972         this.hasFocus = false;
43973         
43974         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43975             this.validate();
43976         }
43977         
43978         var v = this.getValue();
43979         
43980         if(String(v) !== String(this.startValue)){
43981             this.fireEvent('change', this, v, this.startValue);
43982         }
43983         
43984         this.fireEvent("blur", this);
43985     },
43986     
43987     inputEl : function()
43988     {
43989         return this.el.select('.roo-money-amount-input', true).first();
43990     },
43991     
43992     currencyEl : function()
43993     {
43994         return this.el.select('.roo-money-currency-input', true).first();
43995     },
43996     
43997     hiddenEl : function()
43998     {
43999         return this.el.select('input.hidden-number-input',true).first();
44000     }
44001     
44002 });/**
44003  * @class Roo.bootstrap.BezierSignature
44004  * @extends Roo.bootstrap.Component
44005  * Bootstrap BezierSignature class
44006  * This script refer to:
44007  *    Title: Signature Pad
44008  *    Author: szimek
44009  *    Availability: https://github.com/szimek/signature_pad
44010  *
44011  * @constructor
44012  * Create a new BezierSignature
44013  * @param {Object} config The config object
44014  */
44015
44016 Roo.bootstrap.BezierSignature = function(config){
44017     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44018     this.addEvents({
44019         "resize" : true
44020     });
44021 };
44022
44023 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44024 {
44025      
44026     curve_data: [],
44027     
44028     is_empty: true,
44029     
44030     mouse_btn_down: true,
44031     
44032     /**
44033      * @cfg {int} canvas height
44034      */
44035     canvas_height: '200px',
44036     
44037     /**
44038      * @cfg {float|function} Radius of a single dot.
44039      */ 
44040     dot_size: false,
44041     
44042     /**
44043      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44044      */
44045     min_width: 0.5,
44046     
44047     /**
44048      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44049      */
44050     max_width: 2.5,
44051     
44052     /**
44053      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44054      */
44055     throttle: 16,
44056     
44057     /**
44058      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44059      */
44060     min_distance: 5,
44061     
44062     /**
44063      * @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.
44064      */
44065     bg_color: 'rgba(0, 0, 0, 0)',
44066     
44067     /**
44068      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44069      */
44070     dot_color: 'black',
44071     
44072     /**
44073      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44074      */ 
44075     velocity_filter_weight: 0.7,
44076     
44077     /**
44078      * @cfg {function} Callback when stroke begin. 
44079      */
44080     onBegin: false,
44081     
44082     /**
44083      * @cfg {function} Callback when stroke end.
44084      */
44085     onEnd: false,
44086     
44087     getAutoCreate : function()
44088     {
44089         var cls = 'roo-signature column';
44090         
44091         if(this.cls){
44092             cls += ' ' + this.cls;
44093         }
44094         
44095         var col_sizes = [
44096             'lg',
44097             'md',
44098             'sm',
44099             'xs'
44100         ];
44101         
44102         for(var i = 0; i < col_sizes.length; i++) {
44103             if(this[col_sizes[i]]) {
44104                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44105             }
44106         }
44107         
44108         var cfg = {
44109             tag: 'div',
44110             cls: cls,
44111             cn: [
44112                 {
44113                     tag: 'div',
44114                     cls: 'roo-signature-body',
44115                     cn: [
44116                         {
44117                             tag: 'canvas',
44118                             cls: 'roo-signature-body-canvas',
44119                             height: this.canvas_height,
44120                             width: this.canvas_width
44121                         }
44122                     ]
44123                 },
44124                 {
44125                     tag: 'input',
44126                     type: 'file',
44127                     style: 'display: none'
44128                 }
44129             ]
44130         };
44131         
44132         return cfg;
44133     },
44134     
44135     initEvents: function() 
44136     {
44137         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44138         
44139         var canvas = this.canvasEl();
44140         
44141         // mouse && touch event swapping...
44142         canvas.dom.style.touchAction = 'none';
44143         canvas.dom.style.msTouchAction = 'none';
44144         
44145         this.mouse_btn_down = false;
44146         canvas.on('mousedown', this._handleMouseDown, this);
44147         canvas.on('mousemove', this._handleMouseMove, this);
44148         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44149         
44150         if (window.PointerEvent) {
44151             canvas.on('pointerdown', this._handleMouseDown, this);
44152             canvas.on('pointermove', this._handleMouseMove, this);
44153             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44154         }
44155         
44156         if ('ontouchstart' in window) {
44157             canvas.on('touchstart', this._handleTouchStart, this);
44158             canvas.on('touchmove', this._handleTouchMove, this);
44159             canvas.on('touchend', this._handleTouchEnd, this);
44160         }
44161         
44162         Roo.EventManager.onWindowResize(this.resize, this, true);
44163         
44164         // file input event
44165         this.fileEl().on('change', this.uploadImage, this);
44166         
44167         this.clear();
44168         
44169         this.resize();
44170     },
44171     
44172     resize: function(){
44173         
44174         var canvas = this.canvasEl().dom;
44175         var ctx = this.canvasElCtx();
44176         var img_data = false;
44177         
44178         if(canvas.width > 0) {
44179             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44180         }
44181         // setting canvas width will clean img data
44182         canvas.width = 0;
44183         
44184         var style = window.getComputedStyle ? 
44185             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44186             
44187         var padding_left = parseInt(style.paddingLeft) || 0;
44188         var padding_right = parseInt(style.paddingRight) || 0;
44189         
44190         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44191         
44192         if(img_data) {
44193             ctx.putImageData(img_data, 0, 0);
44194         }
44195     },
44196     
44197     _handleMouseDown: function(e)
44198     {
44199         if (e.browserEvent.which === 1) {
44200             this.mouse_btn_down = true;
44201             this.strokeBegin(e);
44202         }
44203     },
44204     
44205     _handleMouseMove: function (e)
44206     {
44207         if (this.mouse_btn_down) {
44208             this.strokeMoveUpdate(e);
44209         }
44210     },
44211     
44212     _handleMouseUp: function (e)
44213     {
44214         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44215             this.mouse_btn_down = false;
44216             this.strokeEnd(e);
44217         }
44218     },
44219     
44220     _handleTouchStart: function (e) {
44221         
44222         e.preventDefault();
44223         if (e.browserEvent.targetTouches.length === 1) {
44224             // var touch = e.browserEvent.changedTouches[0];
44225             // this.strokeBegin(touch);
44226             
44227              this.strokeBegin(e); // assume e catching the correct xy...
44228         }
44229     },
44230     
44231     _handleTouchMove: function (e) {
44232         e.preventDefault();
44233         // var touch = event.targetTouches[0];
44234         // _this._strokeMoveUpdate(touch);
44235         this.strokeMoveUpdate(e);
44236     },
44237     
44238     _handleTouchEnd: function (e) {
44239         var wasCanvasTouched = e.target === this.canvasEl().dom;
44240         if (wasCanvasTouched) {
44241             e.preventDefault();
44242             // var touch = event.changedTouches[0];
44243             // _this._strokeEnd(touch);
44244             this.strokeEnd(e);
44245         }
44246     },
44247     
44248     reset: function () {
44249         this._lastPoints = [];
44250         this._lastVelocity = 0;
44251         this._lastWidth = (this.min_width + this.max_width) / 2;
44252         this.canvasElCtx().fillStyle = this.dot_color;
44253     },
44254     
44255     strokeMoveUpdate: function(e)
44256     {
44257         this.strokeUpdate(e);
44258         
44259         if (this.throttle) {
44260             this.throttleStroke(this.strokeUpdate, this.throttle);
44261         }
44262         else {
44263             this.strokeUpdate(e);
44264         }
44265     },
44266     
44267     strokeBegin: function(e)
44268     {
44269         var newPointGroup = {
44270             color: this.dot_color,
44271             points: []
44272         };
44273         
44274         if (typeof this.onBegin === 'function') {
44275             this.onBegin(e);
44276         }
44277         
44278         this.curve_data.push(newPointGroup);
44279         this.reset();
44280         this.strokeUpdate(e);
44281     },
44282     
44283     strokeUpdate: function(e)
44284     {
44285         var rect = this.canvasEl().dom.getBoundingClientRect();
44286         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44287         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44288         var lastPoints = lastPointGroup.points;
44289         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44290         var isLastPointTooClose = lastPoint
44291             ? point.distanceTo(lastPoint) <= this.min_distance
44292             : false;
44293         var color = lastPointGroup.color;
44294         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44295             var curve = this.addPoint(point);
44296             if (!lastPoint) {
44297                 this.drawDot({color: color, point: point});
44298             }
44299             else if (curve) {
44300                 this.drawCurve({color: color, curve: curve});
44301             }
44302             lastPoints.push({
44303                 time: point.time,
44304                 x: point.x,
44305                 y: point.y
44306             });
44307         }
44308     },
44309     
44310     strokeEnd: function(e)
44311     {
44312         this.strokeUpdate(e);
44313         if (typeof this.onEnd === 'function') {
44314             this.onEnd(e);
44315         }
44316     },
44317     
44318     addPoint:  function (point) {
44319         var _lastPoints = this._lastPoints;
44320         _lastPoints.push(point);
44321         if (_lastPoints.length > 2) {
44322             if (_lastPoints.length === 3) {
44323                 _lastPoints.unshift(_lastPoints[0]);
44324             }
44325             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44326             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44327             _lastPoints.shift();
44328             return curve;
44329         }
44330         return null;
44331     },
44332     
44333     calculateCurveWidths: function (startPoint, endPoint) {
44334         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44335             (1 - this.velocity_filter_weight) * this._lastVelocity;
44336
44337         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44338         var widths = {
44339             end: newWidth,
44340             start: this._lastWidth
44341         };
44342         
44343         this._lastVelocity = velocity;
44344         this._lastWidth = newWidth;
44345         return widths;
44346     },
44347     
44348     drawDot: function (_a) {
44349         var color = _a.color, point = _a.point;
44350         var ctx = this.canvasElCtx();
44351         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44352         ctx.beginPath();
44353         this.drawCurveSegment(point.x, point.y, width);
44354         ctx.closePath();
44355         ctx.fillStyle = color;
44356         ctx.fill();
44357     },
44358     
44359     drawCurve: function (_a) {
44360         var color = _a.color, curve = _a.curve;
44361         var ctx = this.canvasElCtx();
44362         var widthDelta = curve.endWidth - curve.startWidth;
44363         var drawSteps = Math.floor(curve.length()) * 2;
44364         ctx.beginPath();
44365         ctx.fillStyle = color;
44366         for (var i = 0; i < drawSteps; i += 1) {
44367         var t = i / drawSteps;
44368         var tt = t * t;
44369         var ttt = tt * t;
44370         var u = 1 - t;
44371         var uu = u * u;
44372         var uuu = uu * u;
44373         var x = uuu * curve.startPoint.x;
44374         x += 3 * uu * t * curve.control1.x;
44375         x += 3 * u * tt * curve.control2.x;
44376         x += ttt * curve.endPoint.x;
44377         var y = uuu * curve.startPoint.y;
44378         y += 3 * uu * t * curve.control1.y;
44379         y += 3 * u * tt * curve.control2.y;
44380         y += ttt * curve.endPoint.y;
44381         var width = curve.startWidth + ttt * widthDelta;
44382         this.drawCurveSegment(x, y, width);
44383         }
44384         ctx.closePath();
44385         ctx.fill();
44386     },
44387     
44388     drawCurveSegment: function (x, y, width) {
44389         var ctx = this.canvasElCtx();
44390         ctx.moveTo(x, y);
44391         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44392         this.is_empty = false;
44393     },
44394     
44395     clear: function()
44396     {
44397         var ctx = this.canvasElCtx();
44398         var canvas = this.canvasEl().dom;
44399         ctx.fillStyle = this.bg_color;
44400         ctx.clearRect(0, 0, canvas.width, canvas.height);
44401         ctx.fillRect(0, 0, canvas.width, canvas.height);
44402         this.curve_data = [];
44403         this.reset();
44404         this.is_empty = true;
44405     },
44406     
44407     fileEl: function()
44408     {
44409         return  this.el.select('input',true).first();
44410     },
44411     
44412     canvasEl: function()
44413     {
44414         return this.el.select('canvas',true).first();
44415     },
44416     
44417     canvasElCtx: function()
44418     {
44419         return this.el.select('canvas',true).first().dom.getContext('2d');
44420     },
44421     
44422     getImage: function(type)
44423     {
44424         if(this.is_empty) {
44425             return false;
44426         }
44427         
44428         // encryption ?
44429         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44430     },
44431     
44432     drawFromImage: function(img_src)
44433     {
44434         var img = new Image();
44435         
44436         img.onload = function(){
44437             this.canvasElCtx().drawImage(img, 0, 0);
44438         }.bind(this);
44439         
44440         img.src = img_src;
44441         
44442         this.is_empty = false;
44443     },
44444     
44445     selectImage: function()
44446     {
44447         this.fileEl().dom.click();
44448     },
44449     
44450     uploadImage: function(e)
44451     {
44452         var reader = new FileReader();
44453         
44454         reader.onload = function(e){
44455             var img = new Image();
44456             img.onload = function(){
44457                 this.reset();
44458                 this.canvasElCtx().drawImage(img, 0, 0);
44459             }.bind(this);
44460             img.src = e.target.result;
44461         }.bind(this);
44462         
44463         reader.readAsDataURL(e.target.files[0]);
44464     },
44465     
44466     // Bezier Point Constructor
44467     Point: (function () {
44468         function Point(x, y, time) {
44469             this.x = x;
44470             this.y = y;
44471             this.time = time || Date.now();
44472         }
44473         Point.prototype.distanceTo = function (start) {
44474             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44475         };
44476         Point.prototype.equals = function (other) {
44477             return this.x === other.x && this.y === other.y && this.time === other.time;
44478         };
44479         Point.prototype.velocityFrom = function (start) {
44480             return this.time !== start.time
44481             ? this.distanceTo(start) / (this.time - start.time)
44482             : 0;
44483         };
44484         return Point;
44485     }()),
44486     
44487     
44488     // Bezier Constructor
44489     Bezier: (function () {
44490         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44491             this.startPoint = startPoint;
44492             this.control2 = control2;
44493             this.control1 = control1;
44494             this.endPoint = endPoint;
44495             this.startWidth = startWidth;
44496             this.endWidth = endWidth;
44497         }
44498         Bezier.fromPoints = function (points, widths, scope) {
44499             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44500             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44501             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44502         };
44503         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44504             var dx1 = s1.x - s2.x;
44505             var dy1 = s1.y - s2.y;
44506             var dx2 = s2.x - s3.x;
44507             var dy2 = s2.y - s3.y;
44508             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44509             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44510             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44511             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44512             var dxm = m1.x - m2.x;
44513             var dym = m1.y - m2.y;
44514             var k = l2 / (l1 + l2);
44515             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44516             var tx = s2.x - cm.x;
44517             var ty = s2.y - cm.y;
44518             return {
44519                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44520                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44521             };
44522         };
44523         Bezier.prototype.length = function () {
44524             var steps = 10;
44525             var length = 0;
44526             var px;
44527             var py;
44528             for (var i = 0; i <= steps; i += 1) {
44529                 var t = i / steps;
44530                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44531                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44532                 if (i > 0) {
44533                     var xdiff = cx - px;
44534                     var ydiff = cy - py;
44535                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44536                 }
44537                 px = cx;
44538                 py = cy;
44539             }
44540             return length;
44541         };
44542         Bezier.prototype.point = function (t, start, c1, c2, end) {
44543             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44544             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44545             + (3.0 * c2 * (1.0 - t) * t * t)
44546             + (end * t * t * t);
44547         };
44548         return Bezier;
44549     }()),
44550     
44551     throttleStroke: function(fn, wait) {
44552       if (wait === void 0) { wait = 250; }
44553       var previous = 0;
44554       var timeout = null;
44555       var result;
44556       var storedContext;
44557       var storedArgs;
44558       var later = function () {
44559           previous = Date.now();
44560           timeout = null;
44561           result = fn.apply(storedContext, storedArgs);
44562           if (!timeout) {
44563               storedContext = null;
44564               storedArgs = [];
44565           }
44566       };
44567       return function wrapper() {
44568           var args = [];
44569           for (var _i = 0; _i < arguments.length; _i++) {
44570               args[_i] = arguments[_i];
44571           }
44572           var now = Date.now();
44573           var remaining = wait - (now - previous);
44574           storedContext = this;
44575           storedArgs = args;
44576           if (remaining <= 0 || remaining > wait) {
44577               if (timeout) {
44578                   clearTimeout(timeout);
44579                   timeout = null;
44580               }
44581               previous = now;
44582               result = fn.apply(storedContext, storedArgs);
44583               if (!timeout) {
44584                   storedContext = null;
44585                   storedArgs = [];
44586               }
44587           }
44588           else if (!timeout) {
44589               timeout = window.setTimeout(later, remaining);
44590           }
44591           return result;
44592       };
44593   }
44594   
44595 });
44596
44597  
44598
44599